!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!
! parallel_slap.F90 - part of the Community Ice Sheet Model (CISM)
!
!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!
! Copyright (C) 2005-2014
! CISM contributors - see AUTHORS file for list of contributors
!
! This file is part of CISM.
!
! CISM is free software: you can redistribute it and/or modify it
! under the terms of the Lesser GNU General Public License as published
! by the Free Software Foundation, either version 3 of the License, or
! (at your option) any later version.
!
! CISM 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
! Lesser GNU General Public License for more details.
!
! You should have received a copy of the Lesser GNU General Public License
! along with CISM. If not, see .
!
!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
module parallel
use netcdf
use glimmer_global, only : dp, sp
implicit none
!NOTE: The glam/glissade dycore currently requires nhalo = 2,
! whereas the glide dycore requires nhalo = 0.
! For glide simulations, we set nhalo = 0 by calling distributed_grid
! with optional argument nhalo = 0.
integer, save :: nhalo = 2
!TODO - Define lhalo and uhalo in terms of nhalo.
integer, save :: lhalo = 2
integer, save :: uhalo = 2
integer, save :: staggered_lhalo = 2
integer, save :: staggered_uhalo = 1
#ifdef _USE_MPI_WITH_SLAP
logical,save :: main_task
integer,save :: this_rank
integer,save :: tasks
integer,save :: comm
#else
logical,parameter :: main_task = .true.
integer,parameter :: this_rank = 0
integer,parameter :: tasks = 1
#endif
! distributed grid
integer,save :: global_ewn,global_nsn,local_ewn,local_nsn,own_ewn,own_nsn
integer,save :: global_col_offset, global_row_offset
integer,save :: ewlb,ewub,nslb,nsub
integer,save :: east,north,south,west
integer,save :: ewtasks,nstasks
!Note: Currently, periodic_bc is always T, since periodic BCs are always applied.
! Outflow BCs require some additional operations on scalars after periodic BCs are applied.
! No-penetration BCs are enforced by setting velocity masks, without altering halo updates.
! global boundary conditions
logical,save :: periodic_bc ! doubly periodic
logical,save :: outflow_bc ! if true, set scalars in global halo to zero
! does not apply to staggered variables (e.g., uvel, vvel)
! global IDs
integer,parameter :: ProcsEW = 1
!TODO - Remove these gathered_* declarations. No longer used.
! JEFF Declarations for undistributed variables on main_task.
! Later move to separate module? These are only temporary until code is completely distributed.
real(dp),dimension(:,:,:),allocatable :: gathered_efvs ! Output var from glam_velo_fordsiapstr(), used often
real(dp),dimension(:,:,:),allocatable :: gathered_efvs2 ! Variable for testing that scatter/gather are inverses
real(dp),dimension(:,:,:),allocatable :: gathered_uvel ! Output var from glam_velo_fordsiapstr(), used often
real(dp),dimension(:,:,:),allocatable :: gathered_vvel ! Output var from glam_velo_fordsiapstr(), used often
real(dp),dimension(:,:),allocatable :: gathered_uflx ! Output var from glam_velo_fordsiapstr(), used often
real(dp),dimension(:,:),allocatable :: gathered_vflx ! Output var from glam_velo_fordsiapstr(), used often
real(dp),dimension(:,:,:),allocatable :: gathered_velnorm ! Variable calculated in run_ho_diagnostic(), is this used?
real(dp),dimension(:,:),allocatable :: gathered_thck ! Used in horizontal_remap_in()
real(dp),dimension(:,:),allocatable :: gathered_stagthck ! Used in horizontal_remap_in()
real(sp),dimension(:,:),allocatable :: gathered_acab ! Used in horizontal_remap_in()
real(dp),dimension(:,:,:),allocatable :: gathered_temp ! Used in horizontal_remap_in()
real(dp),dimension(:,:),allocatable :: gathered_dusrfdew ! Used in glide_stress()
real(dp),dimension(:,:),allocatable :: gathered_dusrfdns ! Used in glide_stress()
real(dp),dimension(:,:),allocatable :: gathered_dthckdew ! Used in glide_stress()
real(dp),dimension(:,:),allocatable :: gathered_dthckdns ! Used in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauxx ! Calculated in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauyy ! Calculated in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauxy ! Calculated in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauscalar ! Calculated in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauxz ! Calculated in glide_stress()
real(dp),dimension(:,:,:),allocatable :: gathered_tauyz ! Calculated in glide_stress()
real(dp),dimension(:,:),allocatable :: gathered_topg ! Bedrock topology, Used in glide_set_mask()
integer,dimension(:,:),allocatable :: gathered_thkmask ! Calculated in glide_set_mask()
real(dp),dimension(:,:),allocatable :: gathered_marine_bc_normal ! Calculated in glide_marine_margin_normal()
real(dp),dimension(:,:,:),allocatable :: gathered_surfvel ! Used in calc_gline_flux()
real(dp),dimension(:,:),allocatable :: gathered_gline_flux ! Calculated in calc_gline_flux()
real(dp),dimension(:,:),allocatable :: gathered_ubas ! Used in calc_gline_flux()
real(dp),dimension(:,:),allocatable :: gathered_vbas ! Used in calc_gline_flux()
real(dp),dimension(:,:),allocatable :: gathered_relx ! Used in glide_marinlim()
real(dp),dimension(:,:,:),allocatable :: gathered_flwa ! Used in glide_marinlim()
real(sp),dimension(:,:),allocatable :: gathered_calving ! Used in glide_marinlim()
real(sp),dimension(:,:),allocatable :: gathered_backstress ! Used in glide_marinlim()
real(dp),dimension(:,:),allocatable :: gathered_usrf ! Used in glide_marinlim()
logical,dimension(:,:),allocatable :: gathered_backstressmap ! Used in glide_marinlim()
real(dp),dimension(:,:),allocatable :: gathered_tau_x ! Calculated in calc_basal_shear()
real(dp),dimension(:,:),allocatable :: gathered_tau_y ! Calculated in calc_basal_shear()
real(dp),dimension(:,:),allocatable :: gathered_lsrf ! Used in glide_marinlim()
interface broadcast
module procedure broadcast_character
module procedure broadcast_integer
module procedure broadcast_integer_1d
module procedure broadcast_logical
module procedure broadcast_real4
module procedure broadcast_real4_1d
module procedure broadcast_real8
module procedure broadcast_real8_1d
end interface
interface distributed_gather_var
module procedure distributed_gather_var_integer_2d
module procedure distributed_gather_var_logical_2d
module procedure distributed_gather_var_real4_2d
module procedure distributed_gather_var_real4_3d
module procedure distributed_gather_var_real8_2d
module procedure distributed_gather_var_real8_3d
end interface
interface distributed_get_var
module procedure distributed_get_var_integer_2d
module procedure distributed_get_var_real4_1d
module procedure distributed_get_var_real4_2d
module procedure distributed_get_var_real8_1d
module procedure distributed_get_var_real8_2d
module procedure distributed_get_var_real8_3d
end interface
interface distributed_print
! Gathers a distributed variable and writes to file
module procedure distributed_print_integer_2d
module procedure distributed_print_real8_2d
module procedure distributed_print_real8_3d
end interface
interface distributed_put_var
module procedure distributed_put_var_integer_2d
module procedure distributed_put_var_real4_1d
module procedure distributed_put_var_real4_2d
module procedure distributed_put_var_real8_1d
module procedure distributed_put_var_real8_2d
module procedure distributed_put_var_real8_3d
!TODO - Should the parallel_put_var routines be part of this interface?
module procedure parallel_put_var_real4
module procedure parallel_put_var_real8
end interface
interface parallel_convert_haloed_to_nonhaloed
module procedure parallel_convert_haloed_to_nonhaloed_real4_2d
module procedure parallel_convert_haloed_to_nonhaloed_real8_2d
end interface parallel_convert_haloed_to_nonhaloed
interface parallel_convert_nonhaloed_to_haloed
module procedure parallel_convert_nonhaloed_to_haloed_real4_2d
module procedure parallel_convert_nonhaloed_to_haloed_real8_2d
end interface parallel_convert_nonhaloed_to_haloed
interface parallel_def_var
module procedure parallel_def_var_dimids
module procedure parallel_def_var_nodimids
end interface
interface parallel_get_att
module procedure parallel_get_att_character
module procedure parallel_get_att_real4
module procedure parallel_get_att_real4_1d
module procedure parallel_get_att_real8
module procedure parallel_get_att_real8_1d
end interface
interface distributed_scatter_var
module procedure distributed_scatter_var_integer_2d
module procedure distributed_scatter_var_logical_2d
module procedure distributed_scatter_var_real4_2d
module procedure distributed_scatter_var_real4_3d
module procedure distributed_scatter_var_real8_2d
module procedure distributed_scatter_var_real8_3d
end interface
interface global_sum
module procedure global_sum_real8_scalar
module procedure global_sum_real8_1d
end interface
interface parallel_get_var
module procedure parallel_get_var_integer_1d
module procedure parallel_get_var_real4_1d
module procedure parallel_get_var_real8_1d
end interface
interface parallel_halo
module procedure parallel_halo_integer_2d
module procedure parallel_halo_logical_2d
module procedure parallel_halo_real4_2d
module procedure parallel_halo_real8_2d
module procedure parallel_halo_real8_3d
end interface
interface parallel_halo_verify
module procedure parallel_halo_verify_integer_2d
module procedure parallel_halo_verify_real8_2d
module procedure parallel_halo_verify_real8_3d
end interface
interface parallel_print
module procedure parallel_print_integer_2d
module procedure parallel_print_real8_2d
module procedure parallel_print_real8_3d
end interface
interface parallel_put_att
module procedure parallel_put_att_character
module procedure parallel_put_att_real4
module procedure parallel_put_att_real4_1d
module procedure parallel_put_att_real8
module procedure parallel_put_att_real8_1d
end interface
interface parallel_put_var
module procedure parallel_put_var_real4
module procedure parallel_put_var_real8
module procedure parallel_put_var_real8_1d
end interface
interface parallel_reduce_sum
module procedure parallel_reduce_sum_integer
module procedure parallel_reduce_sum_real4
module procedure parallel_reduce_sum_real8
module procedure parallel_reduce_sum_real8_nvar
end interface
interface parallel_reduce_max
module procedure parallel_reduce_max_integer
module procedure parallel_reduce_max_real4
module procedure parallel_reduce_max_real8
end interface
interface parallel_reduce_min
module procedure parallel_reduce_min_integer
module procedure parallel_reduce_min_real4
module procedure parallel_reduce_min_real8
end interface
! This reduce interface determines the global min value and the processor on which it occurs
interface parallel_reduce_maxloc
module procedure parallel_reduce_maxloc_integer
module procedure parallel_reduce_maxloc_real4
module procedure parallel_reduce_maxloc_real8
end interface
! This reduce interface determines the global min value and the processor on which it occurs
interface parallel_reduce_minloc
module procedure parallel_reduce_minloc_integer
module procedure parallel_reduce_minloc_real4
module procedure parallel_reduce_minloc_real8
end interface
interface staggered_parallel_halo
module procedure staggered_parallel_halo_integer_2d
module procedure staggered_parallel_halo_integer_3d
module procedure staggered_parallel_halo_real8_2d
module procedure staggered_parallel_halo_real8_3d
module procedure staggered_parallel_halo_real8_4d
end interface
interface staggered_parallel_halo_extrapolate
module procedure staggered_parallel_halo_extrapolate_integer_2d
module procedure staggered_parallel_halo_extrapolate_real8_2d
end interface
contains
subroutine broadcast_character(c, proc)
implicit none
character(len=*) :: c
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_character
subroutine broadcast_integer(i, proc)
implicit none
integer :: i
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_integer
subroutine broadcast_integer_1d(a, proc)
implicit none
integer,dimension(:) :: a
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_integer_1d
subroutine broadcast_logical(l, proc)
implicit none
logical :: l
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_logical
subroutine broadcast_real4(r, proc)
implicit none
real(sp) :: r
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_real4
subroutine broadcast_real4_1d(a, proc)
real(sp),dimension(:) :: a
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_real4_1d
subroutine broadcast_real8(r, proc)
implicit none
real(dp) :: r
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_real8
subroutine broadcast_real8_1d(a, proc)
implicit none
real(dp),dimension(:) :: a
integer, intent(in), optional :: proc ! optional argument indicating which processor to broadcast from - not relevant to serial version
end subroutine broadcast_real8_1d
function distributed_get_var_integer_2d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_integer_2d,ncid,varid
integer,dimension(:) :: start
integer,dimension(:,:) :: values
integer :: ilo, ihi, jlo, jhi
! begin
if (main_task) then
if (size(values,1)==local_ewn) then
ilo = 1 + lhalo
ihi = local_ewn - uhalo
jlo = 1 + lhalo
jhi = local_nsn - uhalo
else if (size(values,1)==local_ewn-1) then
ilo = 1 + staggered_lhalo
ihi = local_ewn - 1 - uhalo
jlo = 1 + staggered_lhalo
jhi = local_nsn - 1 - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_integer_2d = &
nf90_get_var(ncid,varid,values(ilo:ihi,jlo:jhi),start)
endif
end function distributed_get_var_integer_2d
function distributed_get_var_real4_1d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_real4_1d,ncid,varid
integer,dimension(:) :: start
real(sp),dimension(:) :: values
integer :: status, x1id, y1id
integer :: ilo, ihi
! begin
if (main_task) then
status = nf90_inq_varid(ncid,"x1",x1id)
status = nf90_inq_varid(ncid,"y1",y1id)
if (varid==x1id) then
ilo = 1+lhalo
ihi = local_ewn - uhalo
else if (varid==y1id) then
ilo = 1+lhalo
ihi = local_nsn - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_real4_1d = &
nf90_get_var(ncid,varid,values(ilo:ihi),start)
endif
end function distributed_get_var_real4_1d
function distributed_get_var_real4_2d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_real4_2d,ncid,varid
integer,dimension(:) :: start
real(sp),dimension(:,:) :: values
integer :: ilo, ihi, jlo, jhi
! begin
if (main_task) then
if (size(values,1)==local_ewn) then
ilo = 1 + lhalo
ihi = local_ewn - uhalo
jlo = 1 + lhalo
jhi = local_nsn - uhalo
else if (size(values,1)==local_ewn-1) then
ilo = 1 + lhalo
ihi = local_ewn - 1 - uhalo
jlo = 1 + lhalo
jhi = local_nsn - 1 - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_real4_2d = &
nf90_get_var(ncid,varid,values(ilo:ihi,jlo:jhi),start)
endif
end function distributed_get_var_real4_2d
!WHL - added this function
function distributed_get_var_real8_1d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_real8_1d,ncid,varid
integer,dimension(:) :: start
real(dp),dimension(:) :: values
integer :: status, x1id, y1id
integer :: ilo, ihi
! begin
if (main_task) then
status = nf90_inq_varid(ncid,"x1",x1id)
status = nf90_inq_varid(ncid,"y1",y1id)
if (varid==x1id) then
ilo = 1+lhalo
ihi = local_ewn - uhalo
else if (varid==y1id) then
ilo = 1+lhalo
ihi = local_nsn - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_real8_1d = &
nf90_get_var(ncid,varid,values(ilo:ihi),start)
endif
end function distributed_get_var_real8_1d
function distributed_get_var_real8_2d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_real8_2d,ncid,varid
integer,dimension(:) :: start
real(dp),dimension(:,:) :: values
integer :: ilo, ihi, jlo, jhi
! begin
if (main_task) then
if (size(values,1)==local_ewn) then
ilo = 1 + lhalo
ihi = local_ewn - uhalo
jlo = 1 + lhalo
jhi = local_nsn - uhalo
else if (size(values,1)==local_ewn-1) then
ilo = 1 + lhalo
ihi = local_ewn - 1 - uhalo
jlo = 1 + lhalo
jhi = local_nsn - 1 - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_real8_2d = &
nf90_get_var(ncid,varid,values(ilo:ihi,jlo:jhi),start)
endif
end function distributed_get_var_real8_2d
function distributed_get_var_real8_3d(ncid,varid,values,start)
implicit none
integer :: distributed_get_var_real8_3d,ncid,varid
integer,dimension(:) :: start
real(dp),dimension(:,:,:) :: values
integer :: ilo, ihi, jlo, jhi
! begin
if (main_task) then
if (size(values,1)==local_ewn) then
ilo = 1 + lhalo
ihi = local_ewn - uhalo
jlo = 1 + lhalo
jhi = local_nsn - uhalo
else if (size(values,1)==local_ewn-1) then
ilo = 1 + lhalo
ihi = local_ewn - 1 - uhalo
jlo = 1 + lhalo
jhi = local_nsn - 1 - uhalo
else
call parallel_stop(__FILE__,__LINE__)
end if
distributed_get_var_real8_3d = &
nf90_get_var(ncid,varid,values(ilo:ihi,jlo:jhi,:),start)
endif
end function distributed_get_var_real8_3d
subroutine distributed_grid(ewn, nsn, &
nhalo_in, outflow_bc_in)
implicit none
integer, intent(inout) :: ewn, nsn ! global grid dimensions
integer, intent(in), optional :: nhalo_in ! number of rows of halo cells
logical, intent(in), optional :: outflow_bc_in ! true for outflow global BCs
! (scalars in global halo set to zero)
integer :: ewrank,nsrank
! Optionally, change the halo values
! Note: The higher-order dycores (glam, glissade) currently require nhalo = 2.
! The Glide SIA dycore requires nhalo = 0.
! The default halo values at the top of the module are appropriate for
! the higher-order dycores. Here they can be reset to zero for Glide.
if (present(nhalo_in)) then
if (main_task) then
write(*,*) 'Setting halo values: nhalo =', nhalo_in
if (nhalo_in < 0) then
write(*,*) 'ERROR: nhalo must be >= 0'
call parallel_stop(__FILE__, __LINE__)
endif
endif
nhalo = nhalo_in
lhalo = nhalo
uhalo = nhalo
staggered_lhalo = lhalo
staggered_uhalo = max(uhalo-1, 0)
endif
! initialize some grid quantities to be consistent with parallel_mpi
global_ewn = ewn
global_nsn = nsn
global_row_offset = 0
global_col_offset = 0
ewrank = 0
nsrank = 0
ewtasks = 1
nstasks = 1
east = 0 ! all halo updates are local copies by the main task
west = 0
north = 0
south = 0
! Trey's original code
! ewlb = 1
! ewub = global_ewn
! local_ewn = ewub-ewlb+1
! own_ewn = local_ewn-lhalo-uhalo
! ewn = local_ewn
! nslb = 1
! nsub = global_nsn
! local_nsn = nsub-nslb+1
! own_nsn = local_nsn-lhalo-uhalo
! nsn = local_nsn
!WHL - modified code for nonzero halo values
ewlb = 1 - lhalo
ewub = global_ewn + uhalo
local_ewn = ewub - ewlb + 1
own_ewn = local_ewn - lhalo - uhalo
ewn = local_ewn
nslb = 1 - lhalo
nsub = global_nsn + uhalo
local_nsn = nsub - nslb + 1
own_nsn = local_nsn - lhalo - uhalo
nsn = local_nsn
! set periodic BC as the default
! Note: Currently, it is not strictly necessary to set periodic_bc = T, because
! all halo updates carried out for periodic BC are also carried out for other BCs.
! (Outflow BCs simply add some additional operations on scalars.) ! But setting it here as the default in case that changes.
periodic_bc = .true.
if (present(outflow_bc_in)) then
outflow_bc = outflow_bc_in
else
outflow_bc = .false.
endif
! Print grid geometry
write(*,*) "Process ", this_rank, " Total = ", tasks, " ewtasks = ", ewtasks, " nstasks = ", nstasks
write(*,*) "Process ", this_rank, " ewrank = ", ewrank, " nsrank = ", nsrank
write(*,*) "Process ", this_rank, " l_ewn = ", local_ewn, " o_ewn = ", own_ewn
write(*,*) "Process ", this_rank, " l_nsn = ", local_nsn, " o_nsn = ", own_nsn
write(*,*) "Process ", this_rank, " ewlb = ", ewlb, " ewub = ", ewub
write(*,*) "Process ", this_rank, " nslb = ", nslb, " nsub = ", nsub
write(*,*) "Process ", this_rank, " east = ", east, " west = ", west
write(*,*) "Process ", this_rank, " north = ", north, " south = ", south
write(*,*) "Process ", this_rank, " ew_vars = ", own_ewn, " ns_vars = ", own_nsn
end subroutine distributed_grid
function distributed_execution()
! Returns if running distributed or not.
logical distributed_execution
distributed_execution = .false.
end function distributed_execution
subroutine distributed_gather_var_integer_2d(values, global_values)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
integer,dimension(:,:),intent(in) :: values
integer,dimension(:,:),allocatable,intent(inout) :: global_values
if (allocated(global_values)) then
deallocate(global_values)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(size(values,1), size(values,2)))
!! global_values(:,:) = values(:,:)
allocate(global_values(size(values,1)-uhalo-lhalo, size(values,2)-uhalo-lhalo))
global_values(:,:) = values(1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_integer_2d
subroutine distributed_gather_var_logical_2d(values, global_values)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
logical,dimension(:,:),intent(in) :: values
logical,dimension(:,:),allocatable,intent(inout) :: global_values
if (allocated(global_values)) then
deallocate(global_values)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(size(values,1), size(values,2)))
!! global_values(:,:) = values(:,:)
allocate(global_values(size(values,1)-uhalo-lhalo, size(values,2)-uhalo-lhalo))
global_values(:,:) = values(1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_logical_2d
subroutine distributed_gather_var_real4_2d(values, global_values)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
real(sp),dimension(:,:),intent(in) :: values
real(sp),dimension(:,:),allocatable,intent(inout) :: global_values
if (allocated(global_values)) then
deallocate(global_values)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(size(values,1), size(values,2)))
!! global_values(:,:) = values(:,:)
allocate(global_values(size(values,1)-uhalo-lhalo, size(values,2)-uhalo-lhalo))
global_values(:,:) = values(1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_real4_2d
subroutine distributed_gather_var_real4_3d(values, global_values, ld1, ud1)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
real(sp),dimension(:,:,:),intent(in) :: values
real(sp),dimension(:,:,:),allocatable,intent(inout) :: global_values
integer,optional,intent(in) :: ld1, ud1
integer :: d1l,d1u
if (allocated(global_values)) then
deallocate(global_values)
endif
if (present(ld1)) then
d1l = ld1
else
d1l = 1
endif
if (present(ud1)) then
d1u = ud1
else
d1u = size(values,1)
endif
if (size(values,1) /= d1u-d1l+1) then
write(*,*) "size(values,1) .ne. d1u-d1l+1 in gather call"
call parallel_stop(__FILE__, __LINE__)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(d1l:d1u, size(values,2), size(values,3)))
!! global_values(d1l:d1u,:,:) = values(1:size(values,1),:,:)
allocate(global_values(d1l:d1u, size(values,2)-uhalo-lhalo, size(values,3)-uhalo-lhalo))
global_values(d1l:d1u,:,:) = values(1:size(values,1), 1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_real4_3d
subroutine distributed_gather_var_real8_2d(values, global_values)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
real(dp),dimension(:,:),intent(in) :: values
real(dp),dimension(:,:),allocatable,intent(inout) :: global_values
if (allocated(global_values)) then
deallocate(global_values)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(size(values,1), size(values,2)))
!! global_values(:,:) = values(:,:)
allocate(global_values(size(values,1)-uhalo-lhalo, size(values,2)-uhalo-lhalo))
global_values(:,:) = values(1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_real8_2d
subroutine distributed_gather_var_real8_3d(values, global_values, ld1, ud1)
! JEFF Gather a distributed variable back to main_task node
! values = local portion of distributed variable
! global_values = reference to allocateable array into which the main_task will store the variable.
! If global_values is allocated, then it will be deallocated and reallocated. It will be unused on other nodes.
! Variables are assumed to lie on the scalar grid (at cell centers).
implicit none
real(dp),dimension(:,:,:),intent(in) :: values
real(dp),dimension(:,:,:),allocatable,intent(inout) :: global_values
integer,optional,intent(in) :: ld1, ud1
integer :: d1l,d1u
if (allocated(global_values)) then
deallocate(global_values)
endif
if (present(ld1)) then
d1l = ld1
else
d1l = 1
endif
if (present(ud1)) then
d1u = ud1
else
d1u = size(values,1)
endif
if (size(values,1) /= d1u-d1l+1) then
write(*,*) "size(values,1) .ne. d1u-d1l+1 in gather call"
call parallel_stop(__FILE__, __LINE__)
endif
!WHL - Commented code will not work if the local arrays include halo cells
!! allocate(global_values(d1l:d1u, size(values,2), size(values,3)))
!! global_values(d1l:d1u,:,:) = values(1:size(values,1),:,:)
allocate(global_values(d1l:d1u, size(values,2)-uhalo-lhalo, size(values,3)-uhalo-lhalo))
global_values(d1l:d1u,:,:) = values(1:size(values,1), 1+lhalo:local_ewn-uhalo, 1+lhalo:local_nsn-uhalo)
end subroutine distributed_gather_var_real8_3d
function distributed_isparallel()
implicit none
logical :: distributed_isparallel
distributed_isparallel = .false.
end function distributed_isparallel
function distributed_owner(ew,ewn,ns,nsn)
implicit none
logical :: distributed_owner
integer :: ew,ewn,ns,nsn
! begin
distributed_owner = .true.
end function distributed_owner
subroutine distributed_print_integer_2d(name,values)
implicit none
character(*) :: name
integer,dimension(:,:) :: values
integer,parameter :: u = 33
character(3) :: ts
integer :: i,j,k
write(ts,'(i3.3)') tasks
open(unit=u,file=name//ts//".txt",form="formatted",status="replace")
if (size(values,1) lhalo .and. ilocal <= lhalo + own_ewn) &
.and. &
(jlocal > lhalo .and. jlocal <= lhalo + own_nsn) ) then
! global indices are valid
else ! global indices are invalid
if (main_task) then
write(*,*) 'Invalid global indices: iglobal, jglobal =', iglobal, jglobal
call parallel_stop(__FILE__,__LINE__)
endif
endif
end subroutine parallel_localindex
subroutine parallel_halo_integer_2d(a)
implicit none
integer,dimension(:,:) :: a
integer,dimension(lhalo,local_nsn-lhalo-uhalo) :: ecopy
integer,dimension(uhalo,local_nsn-lhalo-uhalo) :: wcopy
integer,dimension(local_ewn,lhalo) :: ncopy
integer,dimension(local_ewn,uhalo) :: scopy
! begin
! staggered grid
if (size(a,1)==local_ewn-1 .and. size(a,2)==local_nsn-1) return
! unknown grid
if (size(a,1)/=local_ewn .or. size(a,2)/=local_nsn) then
write(*,*) "Unknown Grid: Size a=(", size(a,1), ",", size(a,2), ") and local_ewn and local_nsn = ", &
local_ewn, ",", local_nsn
call parallel_stop(__FILE__,__LINE__)
endif
if (outflow_bc) then
a(:lhalo,1+lhalo:local_nsn-uhalo) = 0
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = 0
a(:,:lhalo) = 0
a(:,local_nsn-uhalo+1:) = 0
else ! periodic BC
ecopy(:,:) = a(local_ewn-uhalo-lhalo+1:local_ewn-uhalo,1+lhalo:local_nsn-uhalo)
wcopy(:,:) = a(1+lhalo:1+lhalo+uhalo-1,1+lhalo:local_nsn-uhalo)
a(:lhalo,1+lhalo:local_nsn-uhalo) = ecopy(:,:)
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = wcopy(:,:)
ncopy(:,:) = a(:,local_nsn-uhalo-lhalo+1:local_nsn-uhalo)
scopy(:,:) = a(:,1+lhalo:1+lhalo+uhalo-1)
a(:,:lhalo) = ncopy(:,:)
a(:,local_nsn-uhalo+1:) = scopy(:,:)
endif
end subroutine parallel_halo_integer_2d
subroutine parallel_halo_logical_2d(a)
implicit none
logical,dimension(:,:) :: a
logical,dimension(lhalo,local_nsn-lhalo-uhalo) :: ecopy
logical,dimension(uhalo,local_nsn-lhalo-uhalo) :: wcopy
logical,dimension(local_ewn,lhalo) :: ncopy
logical,dimension(local_ewn,uhalo) :: scopy
! begin
! staggered grid
if (size(a,1)==local_ewn-1 .and. size(a,2)==local_nsn-1) return
! unknown grid
if (size(a,1)/=local_ewn .or. size(a,2)/=local_nsn) then
write(*,*) "Unknown Grid: Size a=(", size(a,1), ",", size(a,2), ") and local_ewn and local_nsn = ", &
local_ewn, ",", local_nsn
call parallel_stop(__FILE__,__LINE__)
endif
if (outflow_bc) then
a(:lhalo,1+lhalo:local_nsn-uhalo) = .false.
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = .false.
a(:,:lhalo) = .false.
a(:,local_nsn-uhalo+1:) = .false.
else ! periodic BC
ecopy(:,:) = a(local_ewn-uhalo-lhalo+1:local_ewn-uhalo,1+lhalo:local_nsn-uhalo)
wcopy(:,:) = a(1+lhalo:1+lhalo+uhalo-1,1+lhalo:local_nsn-uhalo)
a(:lhalo,1+lhalo:local_nsn-uhalo) = ecopy(:,:)
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = wcopy(:,:)
ncopy(:,:) = a(:,local_nsn-uhalo-lhalo+1:local_nsn-uhalo)
scopy(:,:) = a(:,1+lhalo:1+lhalo+uhalo-1)
a(:,:lhalo) = ncopy(:,:)
a(:,local_nsn-uhalo+1:) = scopy(:,:)
endif
end subroutine parallel_halo_logical_2d
subroutine parallel_halo_real4_2d(a)
implicit none
real(sp),dimension(:,:) :: a
real(sp),dimension(lhalo,local_nsn-lhalo-uhalo) :: ecopy
real(sp),dimension(uhalo,local_nsn-lhalo-uhalo) :: wcopy
real(sp),dimension(local_ewn,lhalo) :: ncopy
real(sp),dimension(local_ewn,uhalo) :: scopy
! begin
! staggered grid
if (size(a,1)==local_ewn-1 .and. size(a,2)==local_nsn-1) return
! unknown grid
if (size(a,1)/=local_ewn .or. size(a,2)/=local_nsn) then
write(*,*) "Unknown Grid: Size a=(", size(a,1), ",", size(a,2), ") and local_ewn and local_nsn = ", &
local_ewn, ",", local_nsn
call parallel_stop(__FILE__,__LINE__)
endif
if (outflow_bc) then
a(:lhalo,1+lhalo:local_nsn-uhalo) = 0.
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = 0.
a(:,:lhalo) = 0.
a(:,local_nsn-uhalo+1:) = 0.
else ! periodic BC
ecopy(:,:) = a(local_ewn-uhalo-lhalo+1:local_ewn-uhalo,1+lhalo:local_nsn-uhalo)
wcopy(:,:) = a(1+lhalo:1+lhalo+uhalo-1,1+lhalo:local_nsn-uhalo)
a(:lhalo,1+lhalo:local_nsn-uhalo) = ecopy(:,:)
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = wcopy(:,:)
ncopy(:,:) = a(:,local_nsn-uhalo-lhalo+1:local_nsn-uhalo)
scopy(:,:) = a(:,1+lhalo:1+lhalo+uhalo-1)
a(:,:lhalo) = ncopy(:,:)
a(:,local_nsn-uhalo+1:) = scopy(:,:)
endif
end subroutine parallel_halo_real4_2d
subroutine parallel_halo_real8_2d(a, periodic_offset_ew, periodic_offset_ns)
!WHL - added optional arguments for periodic offsets, to support ismip-hom test cases
implicit none
real(dp),dimension(:,:) :: a
real(dp), intent(in), optional :: &
periodic_offset_ew, &! offset halo values by this amount
! if positive, the offset is positive for W halo, negative for E halo
periodic_offset_ns ! offset halo values by this amount
! if positive, the offset is positive for S halo, negative for N halo
real(dp),dimension(lhalo,local_nsn-lhalo-uhalo) :: ecopy
real(dp),dimension(uhalo,local_nsn-lhalo-uhalo) :: wcopy
real(dp),dimension(local_ewn,lhalo) :: ncopy
real(dp),dimension(local_ewn,uhalo) :: scopy
! begin
! staggered grid
if (size(a,1)==local_ewn-1 .and. size(a,2)==local_nsn-1) return
! unknown grid
if (size(a,1)/=local_ewn .or. size(a,2)/=local_nsn) then
write(*,*) "Unknown Grid: Size a=(", size(a,1), ",", size(a,2), ") and local_ewn and local_nsn = ", &
local_ewn, ",", local_nsn
call parallel_stop(__FILE__,__LINE__)
endif
if (outflow_bc) then
a(:lhalo,1+lhalo:local_nsn-uhalo) = 0.d0
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = 0.d0
a(:,:lhalo) = 0.d0
a(:,local_nsn-uhalo+1:) = 0.d0
else ! periodic BC
ecopy(:,:) = a(local_ewn-uhalo-lhalo+1:local_ewn-uhalo,1+lhalo:local_nsn-uhalo)
wcopy(:,:) = a(1+lhalo:1+lhalo+uhalo-1,1+lhalo:local_nsn-uhalo)
a(:lhalo,1+lhalo:local_nsn-uhalo) = ecopy(:,:)
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = wcopy(:,:)
if (present(periodic_offset_ew)) then
if (periodic_offset_ew /= 0.d0) then
a(:lhalo,1+lhalo:local_nsn-uhalo) = &
a(:lhalo,1+lhalo:local_nsn-uhalo) + periodic_offset_ew
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = &
a(local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) - periodic_offset_ew
endif
endif
ncopy(:,:) = a(:,local_nsn-uhalo-lhalo+1:local_nsn-uhalo)
scopy(:,:) = a(:,1+lhalo:1+lhalo+uhalo-1)
a(:,:lhalo) = ncopy(:,:)
a(:,local_nsn-uhalo+1:) = scopy(:,:)
if (present(periodic_offset_ns)) then
if (periodic_offset_ns /= 0.d0) then
a(:,:lhalo) = a(:,:lhalo) + periodic_offset_ns
a(:,local_nsn-uhalo+1:) = a(:,local_nsn-uhalo+1:) - periodic_offset_ns
endif
endif
endif ! open or periodic BC
end subroutine parallel_halo_real8_2d
subroutine parallel_halo_real8_3d(a)
implicit none
real(dp),dimension(:,:,:) :: a
real(dp),dimension(size(a,1),lhalo,local_nsn-lhalo-uhalo) :: ecopy
real(dp),dimension(size(a,1),uhalo,local_nsn-lhalo-uhalo) :: wcopy
real(dp),dimension(size(a,1),local_ewn,lhalo) :: ncopy
real(dp),dimension(size(a,1),local_ewn,uhalo) :: scopy
! begin
! staggered grid
if (size(a,1)==local_ewn-1 .and. size(a,2)==local_nsn-1) return
! unknown grid
if (size(a,2)/=local_ewn .or. size(a,3)/=local_nsn) then
write(*,*) "Unknown Grid: Size a=(", size(a,2), ",", size(a,3), ") and local_ewn and local_nsn = ", &
local_ewn, ",", local_nsn
call parallel_stop(__FILE__,__LINE__)
endif
if (outflow_bc) then
a(:,:lhalo,1+lhalo:local_nsn-uhalo) = 0.d0
a(:,local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = 0.d0
a(:,:,:lhalo) = 0.d0
a(:,:,local_nsn-uhalo+1:) = 0.d0
else ! periodic BC
ecopy(:,:,:) = a(:,local_ewn-uhalo-lhalo+1:local_ewn-uhalo,1+lhalo:local_nsn-uhalo)
wcopy(:,:,:) = a(:,1+lhalo:1+lhalo+uhalo-1,1+lhalo:local_nsn-uhalo)
a(:,:lhalo,1+lhalo:local_nsn-uhalo) = ecopy(:,:,:)
a(:,local_ewn-uhalo+1:,1+lhalo:local_nsn-uhalo) = wcopy(:,:,:)
ncopy(:,:,:) = a(:,:,local_nsn-uhalo-lhalo+1:local_nsn-uhalo)
scopy(:,:,:) = a(:,:,1+lhalo:1+lhalo+uhalo-1)
a(:,:,:lhalo) = ncopy(:,:,:)
a(:,:,local_nsn-uhalo+1:) = scopy(:,:,:)
endif
end subroutine parallel_halo_real8_3d
function parallel_halo_verify_integer_2d(a)
implicit none
integer,dimension(:,:) :: a
logical :: parallel_halo_verify_integer_2d
parallel_halo_verify_integer_2d = .true.
end function parallel_halo_verify_integer_2d
function parallel_halo_verify_real8_2d(a)
implicit none
real(dp),dimension(:,:) :: a
logical :: parallel_halo_verify_real8_2d
parallel_halo_verify_real8_2d = .true.
end function parallel_halo_verify_real8_2d
function parallel_halo_verify_real8_3d(a)
implicit none
real(dp),dimension(:,:,:) :: a
logical :: parallel_halo_verify_real8_3d
parallel_halo_verify_real8_3d = .true.
end function parallel_halo_verify_real8_3d
#ifdef _USE_MPI_WITH_SLAP
! parallel_initialise should generally just be called by standalone cism drivers
! When cism is nested inside a climate model (so mpi_init has already been called) use parallel_set_info instead
subroutine parallel_initialise
use mpi_mod
implicit none
integer :: ierror
integer, parameter :: my_main_rank = 0
! begin
call mpi_init(ierror)
call parallel_set_info(mpi_comm_world, my_main_rank)
end subroutine parallel_initialise
! parallel_set_info should be called directly when cism is nested inside a climate model
! (then, mpi_init has already been called, so do NOT use parallel_initialise)
subroutine parallel_set_info(my_comm, my_main_rank)
use mpi_mod
implicit none
integer, intent(in) :: my_comm ! CISM's global communicator
integer, intent(in) :: my_main_rank ! rank of the master task (ignored for parallel_slap)
integer :: ierror
! begin
comm = my_comm
call mpi_comm_size(comm,tasks,ierror)
call mpi_comm_rank(comm,this_rank,ierror)
main_task = .true. !For parallel_slap, each node duplicates all of the calculations.
end subroutine parallel_set_info
#else
subroutine parallel_initialise
implicit none
end subroutine parallel_initialise
subroutine parallel_set_info(my_comm, my_main_rank)
implicit none
integer, intent(in) :: my_comm ! CISM's global communicator (IGNORED)
integer, intent(in) :: my_main_rank ! rank of the master task (IGNORED)
end subroutine parallel_set_info
#endif
subroutine parallel_print_integer_2d(name,values)
implicit none
character(*) :: name
integer,dimension(:,:) :: values
integer,parameter :: u = 33
integer :: i,j
! begin
open(unit=u,file=name//".txt",form="formatted",status="replace")
do j = 1,size(values,2)
do i = 1,size(values,1)
write(u,*) j,i,values(i,j)
end do
write(u,'()')
end do
close(u)
end subroutine parallel_print_integer_2d
function parallel_inq_attname(ncid,varid,attnum,name)
implicit none
integer :: attnum,ncid,parallel_inq_attname,varid
character(len=*) :: name
! begin
if (main_task) parallel_inq_attname = &
nf90_inq_attname(ncid,varid,attnum,name)
call broadcast(parallel_inq_attname)
call broadcast(name)
end function parallel_inq_attname
function parallel_inq_dimid(ncid,name,dimid)
implicit none
integer :: dimid,ncid,parallel_inq_dimid
character(len=*) :: name
! begin
if (main_task) parallel_inq_dimid = nf90_inq_dimid(ncid,name,dimid)
call broadcast(parallel_inq_dimid)
call broadcast(dimid)
end function parallel_inq_dimid
function parallel_inq_varid(ncid,name,varid)
implicit none
integer :: ncid,parallel_inq_varid,varid
character(len=*) :: name
! begin
if (main_task) parallel_inq_varid = nf90_inq_varid(ncid,name,varid)
call broadcast(parallel_inq_varid)
call broadcast(varid)
end function parallel_inq_varid
function parallel_inquire(ncid,nvariables)
implicit none
integer :: ncid,parallel_inquire,nvariables
! begin
if (main_task) parallel_inquire = nf90_inquire(ncid,nvariables=nvariables)
call broadcast(parallel_inquire)
call broadcast(nvariables)
end function parallel_inquire
function parallel_inquire_dimension(ncid,dimid,name,len)
implicit none
integer :: dimid,ncid,parallel_inquire_dimension
integer,optional :: len
character(len=*),optional :: name
integer :: l
! begin
if (present(name)) then
if (main_task) parallel_inquire_dimension = &
nf90_inquire_dimension(ncid,dimid,name,len=l)
call broadcast(name)
else
if (main_task) parallel_inquire_dimension = &
nf90_inquire_dimension(ncid,dimid,len=l)
end if
call broadcast(parallel_inquire_dimension)
if (present(len)) then
call broadcast(l)
len = l
end if
end function parallel_inquire_dimension
function parallel_inquire_variable(ncid,varid,name,ndims,dimids,natts)
implicit none
integer :: ncid,parallel_inquire_variable,varid
integer,optional :: ndims,natts
character(len=*),optional :: name
integer,dimension(:),optional :: dimids
integer :: nd,na
! begin
if (present(name)) then
if (main_task) parallel_inquire_variable = &
nf90_inquire_variable(ncid,varid,name=name)
call broadcast(parallel_inquire_variable)
call broadcast(name)
if (parallel_inquire_variable/=nf90_noerr) return
end if
if (present(dimids)) then
if (main_task) parallel_inquire_variable = &
nf90_inquire_variable(ncid,varid,dimids=dimids)
call broadcast(parallel_inquire_variable)
call broadcast(dimids)
if (parallel_inquire_variable/=nf90_noerr) return
end if
if (main_task) parallel_inquire_variable = &
nf90_inquire_variable(ncid,varid,ndims=nd,natts=na)
call broadcast(parallel_inquire_variable)
if (present(ndims)) then
call broadcast(nd)
ndims = nd
end if
if (present(natts)) then
call broadcast(na)
natts = na
end if
end function parallel_inquire_variable
function parallel_open(path,mode,ncid)
implicit none
integer :: mode,ncid,parallel_open
character(len=*) :: path
! begin
if (main_task) parallel_open = nf90_open(path,mode,ncid)
call broadcast(parallel_open)
end function parallel_open
subroutine parallel_print_real8_2d(name,values)
implicit none
character(*) :: name
real(dp),dimension(:,:) :: values
integer,parameter :: u = 33
integer :: i,j
! begin
open(unit=u,file=name//".txt",form="formatted",status="replace")
do j = 1,size(values,2)
do i = 1,size(values,1)
write(u,*) j,i,values(i,j)
end do
write(u,'()')
end do
close(u)
end subroutine parallel_print_real8_2d
subroutine parallel_print_real8_3d(name,values)
implicit none
character(*) :: name
real(dp),dimension(:,:,:) :: values
integer,parameter :: u = 33
integer :: i,j
! begin
open(unit=u,file=name//".txt",form="formatted",status="replace")
do j = 1,size(values,3)
do i = 1,size(values,2)
write(u,'(2i6,100g15.5e3)') j,i,values(:,i,j)
end do
write(u,'()')
end do
close(u)
end subroutine parallel_print_real8_3d
function parallel_put_att_character(ncid,varid,name,values)
implicit none
integer :: ncid,parallel_put_att_character,varid
character(len=*) :: name,values
! begin
if (main_task) parallel_put_att_character = nf90_put_att(ncid,varid,name,values)
call broadcast(parallel_put_att_character)
end function parallel_put_att_character
function parallel_put_att_real4(ncid,varid,name,values)
implicit none
integer :: ncid,parallel_put_att_real4,varid
character(len=*) :: name
real(sp) :: values
! begin
if (main_task) parallel_put_att_real4 = nf90_put_att(ncid,varid,name,values)
call broadcast(parallel_put_att_real4)
end function parallel_put_att_real4
function parallel_put_att_real4_1d(ncid,varid,name,values)
implicit none
integer :: ncid,parallel_put_att_real4_1d,varid
character(len=*) :: name
real(sp),dimension(:) :: values
! begin
if (main_task) parallel_put_att_real4_1d = nf90_put_att(ncid,varid,name,values)
call broadcast(parallel_put_att_real4_1d)
end function parallel_put_att_real4_1d
function parallel_put_att_real8(ncid,varid,name,values)
implicit none
integer :: ncid,parallel_put_att_real8,varid
character(len=*) :: name
real(dp) :: values
! begin
if (main_task) parallel_put_att_real8 = nf90_put_att(ncid,varid,name,values)
call broadcast(parallel_put_att_real8)
end function parallel_put_att_real8
function parallel_put_att_real8_1d(ncid,varid,name,values)
implicit none
integer :: ncid,parallel_put_att_real8_1d,varid
character(len=*) :: name
real(dp),dimension(:) :: values
! begin
if (main_task) parallel_put_att_real8_1d = nf90_put_att(ncid,varid,name,values)
call broadcast(parallel_put_att_real8_1d)
end function parallel_put_att_real8_1d
function parallel_put_var_real4(ncid,varid,values,start)
implicit none
integer :: ncid,parallel_put_var_real4,varid
integer,dimension(:) :: start
real(sp) :: values
! begin
if (main_task) parallel_put_var_real4 = &
nf90_put_var(ncid,varid,values,start)
call broadcast(parallel_put_var_real4)
end function parallel_put_var_real4
function parallel_put_var_real8(ncid,varid,values,start)
implicit none
integer :: ncid,parallel_put_var_real8,varid
integer,dimension(:) :: start
real(dp) :: values
! begin
if (main_task) parallel_put_var_real8 = &
nf90_put_var(ncid,varid,values,start)
call broadcast(parallel_put_var_real8)
end function parallel_put_var_real8
function parallel_put_var_real8_1d(ncid,varid,values,start)
implicit none
integer :: ncid,parallel_put_var_real8_1d,varid
integer,dimension(:),optional :: start
real(dp),dimension(:) :: values
! begin
if (main_task) then
if (present(start)) then
parallel_put_var_real8_1d = nf90_put_var(ncid,varid,values,start)
else
parallel_put_var_real8_1d = nf90_put_var(ncid,varid,values)
end if
end if
call broadcast(parallel_put_var_real8_1d)
end function parallel_put_var_real8_1d
function parallel_redef(ncid)
implicit none
integer :: ncid,parallel_redef
! begin
if (main_task) parallel_redef = nf90_redef(ncid)
call broadcast(parallel_redef)
end function parallel_redef
! ------------------------------------------
! functions for parallel_reduce_sum interface
! ------------------------------------------
function parallel_reduce_sum_integer(x)
! Sum x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
integer :: x, parallel_reduce_sum_integer
parallel_reduce_sum_integer = x
return
end function parallel_reduce_sum_integer
function parallel_reduce_sum_real4(x)
! Sum x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(sp) :: x, parallel_reduce_sum_real4
parallel_reduce_sum_real4 = x
return
end function parallel_reduce_sum_real4
function parallel_reduce_sum_real8(x)
! Sum x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(dp) :: x, parallel_reduce_sum_real8
parallel_reduce_sum_real8 = x
return
end function parallel_reduce_sum_real8
function parallel_reduce_sum_real8_nvar(x)
! Sum x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(dp) :: x(:)
real(dp), dimension(size(x)) :: parallel_reduce_sum_real8_nvar
parallel_reduce_sum_real8_nvar(:) = x(:)
return
end function parallel_reduce_sum_real8_nvar
! ------------------------------------------
! functions for parallel_reduce_max interface
! ------------------------------------------
function parallel_reduce_max_integer(x)
! Max x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
integer :: x, parallel_reduce_max_integer
parallel_reduce_max_integer = x
return
end function parallel_reduce_max_integer
function parallel_reduce_max_real4(x)
! Max x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(sp) :: x, parallel_reduce_max_real4
parallel_reduce_max_real4 = x
return
end function parallel_reduce_max_real4
function parallel_reduce_max_real8(x)
! Max x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(dp) :: x, parallel_reduce_max_real8
parallel_reduce_max_real8 = x
return
end function parallel_reduce_max_real8
! ------------------------------------------
! routines for parallel_reduce_maxloc interface
! ------------------------------------------
subroutine parallel_reduce_maxloc_integer(xin, xout, xprocout)
! Max x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
integer, intent(in) :: xin ! variable to reduce
integer, intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_maxloc_integer
subroutine parallel_reduce_maxloc_real4(xin, xout, xprocout)
! Max x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
real(sp), intent(in) :: xin ! variable to reduce
real(sp), intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_maxloc_real4
subroutine parallel_reduce_maxloc_real8(xin, xout, xprocout)
! Max x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
real(dp), intent(in) :: xin ! variable to reduce
real(dp), intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_maxloc_real8
! ------------------------------------------
! functions for parallel_reduce_min interface
! ------------------------------------------
function parallel_reduce_min_integer(x)
! Min x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
integer :: x, parallel_reduce_min_integer
parallel_reduce_min_integer = x
return
end function parallel_reduce_min_integer
function parallel_reduce_min_real4(x)
! Min x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(sp) :: x, parallel_reduce_min_real4
parallel_reduce_min_real4 = x
return
end function parallel_reduce_min_real4
function parallel_reduce_min_real8(x)
! Min x across all of the nodes.
! In parallel_slap mode just return x.
implicit none
real(dp) :: x, parallel_reduce_min_real8
parallel_reduce_min_real8 = x
return
end function parallel_reduce_min_real8
! ------------------------------------------
! routines for parallel_reduce_minloc interface
! ------------------------------------------
subroutine parallel_reduce_minloc_integer(xin, xout, xprocout)
! Min x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
integer, intent(in) :: xin ! variable to reduce
integer, intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_minloc_integer
subroutine parallel_reduce_minloc_real4(xin, xout, xprocout)
! Min x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
real(sp), intent(in) :: xin ! variable to reduce
real(sp), intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_minloc_real4
subroutine parallel_reduce_minloc_real8(xin, xout, xprocout)
! Min x across all of the nodes and its proc number
! In parallel_slap mode just return x.
implicit none
real(dp), intent(in) :: xin ! variable to reduce
real(dp), intent(out) :: xout ! value resulting from the reduction
integer, intent(out) :: xprocout ! processor on which reduced value occurs
xout = xin
xprocout = this_rank
end subroutine parallel_reduce_minloc_real8
subroutine parallel_show_minmax(label,values)
implicit none
character(*) :: label
real(dp),dimension(:,:,:) :: values
! begin
print *,label,minval(values),maxval(values)
end subroutine parallel_show_minmax
subroutine parallel_stop(file,line)
implicit none
integer :: line
character(len=*) :: file
! begin
write(0,*) "STOP in ",file," at line ",line
stop
end subroutine parallel_stop
function parallel_sync(ncid)
implicit none
integer :: ncid,parallel_sync
! begin
if (main_task) parallel_sync = nf90_sync(ncid)
call broadcast(parallel_sync)
end function parallel_sync
subroutine staggered_no_penetration_mask(umask, vmask)
implicit none
integer,dimension(:,:) :: umask, vmask ! mask set to 1 wherever the outflow velocity should be zero
! initialize the no-penetration masks to 0
umask(:,:) = 0
vmask(:,:) = 0
! set u velocity mask = 1 at the east global boundary and vertices eastward
umask(local_ewn-uhalo:,:) = 1
! set u velocity mask = 1 at the west global boundary and vertices westward
umask(:lhalo,:) = 1
! set v velocity mask = 1 at the north global boundary and vertices northward
vmask(:,local_nsn-uhalo:) = 1
! set v velocity mask = 1 at the south global boundary and vertices southward
vmask(:,:lhalo) = 1
call staggered_parallel_halo(umask)
call staggered_parallel_halo(vmask)
end subroutine staggered_no_penetration_mask
subroutine staggered_parallel_halo_extrapolate_integer_2d(a)
implicit none
integer,dimension(:,:) :: a
integer :: i, j
! begin
! Confirm staggered array
if (size(a,1)/=local_ewn-1 .or. size(a,2)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
! Extrapolate the staggered field into halo cells along the global boundary.
! Currently this is used only for kinbcmask.
! Note: The extrapolation region includes locally owned cells along
! the north and east boundaries of the global domain.
! extrapolate westward
do i = 1, staggered_lhalo
a(i, staggered_lhalo+1:size(a,2)-staggered_uhalo-1) = &
a(staggered_lhalo+1, staggered_lhalo+1:size(a,2)-staggered_uhalo-1)
enddo
! extrapolate eastward
do i = size(a,1)-staggered_uhalo, size(a,1)
a(i, staggered_lhalo+1:size(a,2)-staggered_uhalo-1) = &
a(size(a,1)-staggered_uhalo-1, staggered_lhalo+1:size(a,2)-staggered_uhalo-1)
enddo
! extrapolate southward
do j = 1, staggered_lhalo
a(1:size(a,1), j) = a(1:size(a,1), staggered_lhalo+1)
enddo
! extrapolate northward
do j = size(a,2)-staggered_uhalo, size(a,2)
a(1:size(a,1), j) = a(1:size(a,1), size(a,2)-staggered_uhalo-1)
enddo
end subroutine staggered_parallel_halo_extrapolate_integer_2d
subroutine staggered_parallel_halo_extrapolate_real8_2d(a)
implicit none
real(dp),dimension(:,:) :: a
integer :: i, j
! begin
! Confirm staggered array
if (size(a,1)/=local_ewn-1 .or. size(a,2)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
! Extrapolate the staggered field into halo cells along the global boundary.
! Currently this is used only for kinbcmask.
! Note: The extrapolation region includes locally owned cells along
! the north and east boundaries of the global domain.
! extrapolate westward
do i = 1, staggered_lhalo
a(i, staggered_lhalo+1:size(a,2)-staggered_uhalo-1) = &
a(staggered_lhalo+1, staggered_lhalo+1:size(a,2)-staggered_uhalo-1)
enddo
! extrapolate eastward
do i = size(a,1)-staggered_uhalo, size(a,1)
a(i, staggered_lhalo+1:size(a,2)-staggered_uhalo-1) = &
a(size(a,1)-staggered_uhalo-1, staggered_lhalo+1:size(a,2)-staggered_uhalo-1)
enddo
! extrapolate southward
do j = 1, staggered_lhalo
a(1:size(a,1), j) = a(1:size(a,1), staggered_lhalo+1)
enddo
! extrapolate northward
do j = size(a,2)-staggered_uhalo, size(a,2)
a(1:size(a,1), j) = a(1:size(a,1), size(a,2)-staggered_uhalo-1)
enddo
end subroutine staggered_parallel_halo_extrapolate_real8_2d
subroutine staggered_parallel_halo_integer_2d(a)
implicit none
integer,dimension(:,:) :: a
integer,dimension(staggered_lhalo,size(a,2)-staggered_lhalo-staggered_uhalo) :: ecopy
integer,dimension(staggered_uhalo,size(a,2)-staggered_lhalo-staggered_uhalo) :: wcopy
integer,dimension(size(a,1),staggered_lhalo) :: ncopy
integer,dimension(size(a,1),staggered_uhalo) :: scopy
! begin
! Confirm staggered array
if (size(a,1)/=local_ewn-1 .or. size(a,2)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
wcopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo) = &
a(1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1, &
1+staggered_lhalo:size(a,2)-staggered_uhalo)
ecopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo) = &
a(size(a,1)-staggered_uhalo-staggered_lhalo+1:size(a,1)-staggered_uhalo, &
1+staggered_lhalo:size(a,2)-staggered_uhalo)
a(size(a,1)-staggered_uhalo+1:size(a,1), 1+staggered_lhalo:size(a,2)-staggered_uhalo) = &
wcopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo)
a(1:staggered_lhalo, 1+staggered_lhalo:size(a,2)-staggered_uhalo) = &
ecopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo)
scopy(:,:) = a(:, 1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1)
ncopy(:,:) = a(:, size(a,2)-staggered_uhalo-staggered_lhalo+1:size(a,2)-staggered_uhalo)
a(:, size(a,2)-staggered_uhalo+1:size(a,2)) = scopy(:,:)
a(:, 1:staggered_lhalo) = ncopy(:,:)
end subroutine staggered_parallel_halo_integer_2d
subroutine staggered_parallel_halo_integer_3d(a)
implicit none
integer,dimension(:,:,:) :: a
integer,dimension(size(a,1),staggered_lhalo,size(a,3)-staggered_lhalo-staggered_uhalo) :: ecopy
integer,dimension(size(a,1),staggered_uhalo,size(a,3)-staggered_lhalo-staggered_uhalo) :: wcopy
integer,dimension(size(a,1),size(a,2),staggered_lhalo) :: ncopy
integer,dimension(size(a,1),size(a,2),staggered_uhalo) :: scopy
! begin
! Confirm staggered array
if (size(a,2)/=local_ewn-1 .or. size(a,3)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
wcopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo) = &
a(:,1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1, &
1+staggered_lhalo:size(a,3)-staggered_uhalo)
ecopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo) = &
a(:,size(a,2)-staggered_uhalo-staggered_lhalo+1:size(a,2)-staggered_uhalo, &
1+staggered_lhalo:size(a,3)-staggered_uhalo)
a(:, size(a,2)-staggered_uhalo+1:size(a,2), 1+staggered_lhalo:size(a,3)-staggered_uhalo) = &
wcopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo)
a(:, 1:staggered_lhalo, 1+staggered_lhalo:size(a,3)-staggered_uhalo) = &
ecopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo)
scopy(:,:,:) = a(:,:, 1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1)
ncopy(:,:,:) = a(:,:, size(a,3)-staggered_uhalo-staggered_lhalo+1:size(a,3)-staggered_uhalo)
a(:,:,size(a,3)-staggered_uhalo+1:size(a,3)) = scopy(:,:,:)
a(:,:,1:staggered_lhalo) = ncopy(:,:,:)
end subroutine staggered_parallel_halo_integer_3d
subroutine staggered_parallel_halo_real8_2d(a)
implicit none
real(dp),dimension(:,:) :: a
real(dp),dimension(staggered_lhalo,size(a,2)-staggered_lhalo-staggered_uhalo) :: ecopy
real(dp),dimension(staggered_uhalo,size(a,2)-staggered_lhalo-staggered_uhalo) :: wcopy
real(dp),dimension(size(a,1),staggered_lhalo) :: ncopy
real(dp),dimension(size(a,1),staggered_uhalo) :: scopy
! begin
! Confirm staggered array
if (size(a,1)/=local_ewn-1 .or. size(a,2)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
wcopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo) = &
a(1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1, &
1+staggered_lhalo:size(a,2)-staggered_uhalo)
ecopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo) = &
a(size(a,1)-staggered_uhalo-staggered_lhalo+1:size(a,1)-staggered_uhalo, &
1+staggered_lhalo:size(a,2)-staggered_uhalo)
a(size(a,1)-staggered_uhalo+1:size(a,1), 1+staggered_lhalo:size(a,2)-staggered_uhalo) = &
wcopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo)
a(1:staggered_lhalo, 1+staggered_lhalo:size(a,2)-staggered_uhalo) = &
ecopy(:, 1:size(a,2)-staggered_lhalo-staggered_uhalo)
scopy(:,:) = a(:, 1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1)
ncopy(:,:) = a(:, size(a,2)-staggered_uhalo-staggered_lhalo+1:size(a,2)-staggered_uhalo)
a(:, size(a,2)-staggered_uhalo+1:size(a,2)) = scopy(:,:)
a(:, 1:staggered_lhalo) = ncopy(:,:)
end subroutine staggered_parallel_halo_real8_2d
subroutine staggered_parallel_halo_real8_3d(a)
implicit none
real(dp),dimension(:,:,:) :: a
real(dp),dimension(size(a,1),staggered_lhalo,size(a,3)-staggered_lhalo-staggered_uhalo) :: ecopy
real(dp),dimension(size(a,1),staggered_uhalo,size(a,3)-staggered_lhalo-staggered_uhalo) :: wcopy
real(dp),dimension(size(a,1),size(a,2),staggered_lhalo) :: ncopy
real(dp),dimension(size(a,1),size(a,2),staggered_uhalo) :: scopy
! begin
! Confirm staggered array
if (size(a,2)/=local_ewn-1 .or. size(a,3)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
wcopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo) = &
a(:,1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1, &
1+staggered_lhalo:size(a,3)-staggered_uhalo)
ecopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo) = &
a(:,size(a,2)-staggered_uhalo-staggered_lhalo+1:size(a,2)-staggered_uhalo, &
1+staggered_lhalo:size(a,3)-staggered_uhalo)
a(:, size(a,2)-staggered_uhalo+1:size(a,2), 1+staggered_lhalo:size(a,3)-staggered_uhalo) = &
wcopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo)
a(:, 1:staggered_lhalo, 1+staggered_lhalo:size(a,3)-staggered_uhalo) = &
ecopy(:,:, 1:size(a,3)-staggered_lhalo-staggered_uhalo)
scopy(:,:,:) = a(:,:, 1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1)
ncopy(:,:,:) = a(:,:, size(a,3)-staggered_uhalo-staggered_lhalo+1:size(a,3)-staggered_uhalo)
a(:,:,size(a,3)-staggered_uhalo+1:size(a,3)) = scopy(:,:,:)
a(:,:,1:staggered_lhalo) = ncopy(:,:,:)
end subroutine staggered_parallel_halo_real8_3d
!WHL - New subroutine for 4D arrays
subroutine staggered_parallel_halo_real8_4d(a)
! Implements a staggered grid halo update for a 4D field.
! This subroutine is used for the 4D arrays that hold matrix entries.
! As the grid is staggered, the array 'a' is one smaller in x and y dimensions than an unstaggered array.
! The vertical dimension is assumed to precede the i and j indices, i.e., a(:,k,i,j).
! The first dimension holds matrix elements for a single row.
implicit none
real(dp),dimension(:,:,:,:) :: a
real(dp),dimension(size(a,1),size(a,2),staggered_lhalo,size(a,4)-staggered_lhalo-staggered_uhalo) :: ecopy
real(dp),dimension(size(a,1),size(a,2),staggered_uhalo,size(a,4)-staggered_lhalo-staggered_uhalo) :: wcopy
real(dp),dimension(size(a,1),size(a,2),size(a,3),staggered_lhalo) :: ncopy
real(dp),dimension(size(a,1),size(a,2),size(a,3),staggered_uhalo) :: scopy
! begin
! Confirm staggered array
if (size(a,3)/=local_ewn-1 .or. size(a,4)/=local_nsn-1) then
write(*,*) "staggered_parallel_halo() requires staggered arrays."
call parallel_stop(__FILE__,__LINE__)
endif
wcopy(:,:,:, 1:size(a,4)-staggered_lhalo-staggered_uhalo) = &
a(:,:,1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1, &
1+staggered_lhalo:size(a,4)-staggered_uhalo)
ecopy(:,:,:, 1:size(a,4)-staggered_lhalo-staggered_uhalo) = &
a(:,:,size(a,3)-staggered_uhalo-staggered_lhalo+1:size(a,3)-staggered_uhalo, &
1+staggered_lhalo:size(a,4)-staggered_uhalo)
a(:,:, size(a,3)-staggered_uhalo+1:size(a,3), 1+staggered_lhalo:size(a,4)-staggered_uhalo) = &
wcopy(:,:,:, 1:size(a,4)-staggered_lhalo-staggered_uhalo)
a(:,:, 1:staggered_lhalo, 1+staggered_lhalo:size(a,4)-staggered_uhalo) = &
ecopy(:,:,:, 1:size(a,4)-staggered_lhalo-staggered_uhalo)
scopy(:,:,:,:) = a(:,:,:, 1+staggered_lhalo:1+staggered_lhalo+staggered_uhalo-1)
ncopy(:,:,:,:) = a(:,:,:, size(a,4)-staggered_uhalo-staggered_lhalo+1:size(a,4)-staggered_uhalo)
a(:,:,:,size(a,4)-staggered_uhalo+1:size(a,4)) = scopy(:,:,:,:)
a(:,:,:,1:staggered_lhalo) = ncopy(:,:,:,:)
end subroutine staggered_parallel_halo_real8_4d
end module parallel