INTEGER io_form , Hndl
LOGICAL :: for_out
INTEGER, EXTERNAL :: use_package
LOGICAL, EXTERNAL :: wrf_dm_on_monitor, multi_files, use_output_servers
#ifdef NETCDF
EXTERNAL ext_ncd_write_field
#endif
#ifdef MCELIO
EXTERNAL ext_mcel_write_field
#endif
#ifdef ESMFIO
EXTERNAL ext_esmf_write_field
#endif
#ifdef INTIO
EXTERNAL ext_int_write_field
#endif
#ifdef XXX
EXTERNAL ext_xxx_write_field
#endif
#ifdef YYY
EXTERNAL ext_yyy_write_field
#endif
#ifdef GRIB1
EXTERNAL ext_gr1_write_field
#endif
#ifdef GRIB2
EXTERNAL ext_gr2_write_field
#endif
CALL wrf_debug( DEBUG_LVL, 'module_io.F: in wrf_write_field' )
Status = 0
CALL get_handle ( Hndl, io_form , for_out, DataHandle )
CALL reset_first_operation ( DataHandle )
IF ( Hndl .GT. -1 ) THEN
IF ( multi_files( io_form ) .OR. .NOT. use_output_servers() ) THEN
SELECT CASE ( use_package( io_form ) )
#ifdef NETCDF
CASE ( IO_NETCDF )
CALL collect_fld_and_call_pkg ( ext_ncd_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef MCELIO
CASE ( IO_MCEL )
CALL collect_fld_and_call_pkg ( ext_mcel_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef ESMFIO
CASE ( IO_ESMF )
CALL ext_esmf_write_field( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef PHDF5
CASE ( IO_PHDF5 )
CALL ext_phdf5_write_field( &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef XXX
CASE ( IO_XXX )
CALL collect_fld_and_call_pkg ( ext_xxx_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef YYY
CASE ( IO_YYY )
CALL collect_fld_and_call_pkg ( ext_yyy_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef GRIB1
CASE ( IO_GRIB1 )
CALL collect_fld_and_call_pkg ( ext_gr1_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef GRIB2
CASE ( IO_GRIB2 )
CALL collect_fld_and_call_pkg ( ext_gr2_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
#ifdef INTIO
CASE ( IO_INTIO )
CALL collect_fld_and_call_pkg ( ext_int_write_field, multi_files(io_form), &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
#endif
CASE DEFAULT
Status = 0
END SELECT
ELSE IF ( use_output_servers() ) THEN
IF ( io_form .GT. 0 ) THEN
CALL wrf_quilt_write_field ( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ENDIF
ENDIF
ELSE
Status = WRF_ERR_FATAL_BAD_FILE_STATUS
ENDIF
RETURN
END SUBROUTINE wrf_write_field1
SUBROUTINE get_value_from_pairs ( varname , str , retval )
!
!
! parse comma separated list of VARIABLE=VALUE strings and return the
! value for the matching variable if such exists, otherwise return
! the empty string
!
!
IMPLICIT NONE
CHARACTER*(*) :: varname
CHARACTER*(*) :: str
CHARACTER*(*) :: retval
CHARACTER (128) varstr, tstr
INTEGER i,j,n,varstrn
LOGICAL nobreak, nobreakouter
varstr = TRIM(varname)//"="
varstrn = len(TRIM(varstr))
n = len(str)
retval = ""
i = 1
nobreakouter = .TRUE.
DO WHILE ( nobreakouter )
j = 1
nobreak = .TRUE.
tstr = ""
! Potential for out of bounds array ref on str(i:i) for i > n; reported by jedwards
! DO WHILE ( nobreak )
! IF ( str(i:i) .NE. ',' .AND. i .LE. n ) THEN
! tstr(j:j) = str(i:i)
! ELSE
! nobreak = .FALSE.
! ENDIF
! j = j + 1
! i = i + 1
! ENDDO
! fix 20021112, JM
DO WHILE ( nobreak )
nobreak = .FALSE.
IF ( i .LE. n ) THEN
IF (str(i:i) .NE. ',' ) THEN
tstr(j:j) = str(i:i)
nobreak = .TRUE.
ENDIF
ENDIF
j = j + 1
i = i + 1
ENDDO
IF ( i .GT. n ) nobreakouter = .FALSE.
IF ( varstr(1:varstrn) .EQ. tstr(1:varstrn) ) THEN
retval(1:) = TRIM(tstr(varstrn+1:))
nobreakouter = .FALSE.
ENDIF
ENDDO
RETURN
END SUBROUTINE get_value_from_pairs
LOGICAL FUNCTION multi_files ( io_form )
!
!
! Returns .TRUE. iff io_form is a multi-file format. A multi-file format
! results in one file for each compute process and can be used with any
! I/O package. A multi-file dataset can only be read by the same number
! of tasks that were used to write it. This feature can be useful for
! speeding up restarts on machines that support efficient parallel I/O.
! Multi-file formats cannot be used with I/O quilt servers.
!
!
IMPLICIT NONE
INTEGER, INTENT(IN) :: io_form
#ifdef DM_PARALLEL
multi_files = io_form > 99
#else
multi_files = .FALSE.
#endif
END FUNCTION multi_files
INTEGER FUNCTION use_package ( io_form )
!
!
! Returns the ID of the external I/O package referenced by io_form.
!
!
IMPLICIT NONE
INTEGER, INTENT(IN) :: io_form
use_package = MOD( io_form, 100 )
END FUNCTION use_package
SUBROUTINE collect_fld_and_call_pkg ( fcn, donotcollect_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! This routine handle cases where collection can be skipped and deals with
! different data types for Field.
!
!
IMPLICIT NONE
include 'wrf_io_flags.h'
EXTERNAL fcn
LOGICAL, INTENT(IN) :: donotcollect_arg
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
INTEGER , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(OUT) :: Status
LOGICAL donotcollect
INTEGER ndims, nproc
CALL dim_from_memorder( MemoryOrder , ndims)
CALL wrf_get_nproc( nproc )
donotcollect = donotcollect_arg .OR. (nproc .EQ. 1)
IF ( donotcollect ) THEN
CALL fcn ( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL collect_double_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL collect_real_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL collect_int_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
CALL collect_logical_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ENDIF
RETURN
END SUBROUTINE collect_fld_and_call_pkg
SUBROUTINE collect_real_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! The sole purpose of this wrapper is to allocate a big real buffer and
! pass it down to collect_generic_and_call_pkg() to do the actual work.
!
!
USE module_state_description
USE module_driver_constants
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
REAL , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
REAL, ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
#ifdef DEREF_KLUDGE
# define FRSTELEM (1)
#else
# define FRSTELEM
#endif
CALL collect_generic_and_call_pkg ( fcn, globbuf FRSTELEM, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE collect_real_and_call_pkg
SUBROUTINE collect_int_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! The sole purpose of this wrapper is to allocate a big integer buffer and
! pass it down to collect_generic_and_call_pkg() to do the actual work.
!
!
USE module_state_description
USE module_driver_constants
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
INTEGER , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
INTEGER, ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
CALL collect_generic_and_call_pkg ( fcn, globbuf FRSTELEM , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE collect_int_and_call_pkg
SUBROUTINE collect_double_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! The sole purpose of this wrapper is to allocate a big double precision
! buffer and pass it down to collect_generic_and_call_pkg() to do the
! actual work.
!
!
USE module_state_description
USE module_driver_constants
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
DOUBLE PRECISION , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
DOUBLE PRECISION, ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
CALL collect_generic_and_call_pkg ( fcn, globbuf FRSTELEM , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE collect_double_and_call_pkg
SUBROUTINE collect_logical_and_call_pkg ( fcn, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! The sole purpose of this wrapper is to allocate a big logical buffer
! and pass it down to collect_generic_and_call_pkg() to do the actual work.
!
!
USE module_state_description
USE module_driver_constants
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
LOGICAL , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
LOGICAL, ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
CALL collect_generic_and_call_pkg ( fcn, globbuf FRSTELEM , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE collect_logical_and_call_pkg
SUBROUTINE collect_generic_and_call_pkg ( fcn, globbuf, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The collect_*_and_call_pkg routines collect a distributed array onto one
! processor and then call an I/O function to write the result (or in the
! case of replicated data simply write monitor node's copy of the data)
! This routine calls the distributed memory communication routines that
! collect the array and then calls I/O function fcn to write it to disk.
!
!
USE module_state_description
USE module_driver_constants
IMPLICIT NONE
include 'wrf_io_flags.h'
#if defined( DM_PARALLEL ) && ! defined(STUBMPI)
include "mpif.h"
#endif
EXTERNAL fcn
REAL , DIMENSION(*) , INTENT(INOUT) :: globbuf
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
REAL , INTENT(IN) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(OUT) :: Status
CHARACTER*3 MemOrd
LOGICAL, EXTERNAL :: has_char
INTEGER ids, ide, jds, jde, kds, kde
INTEGER ims, ime, jms, jme, kms, kme
INTEGER ips, ipe, jps, jpe, kps, kpe
INTEGER nproc, communicator, displs(10*1024), mpi_bdyslice_type, ierr, my_displ, recv_count, root_proc, send_count, itype
INTEGER my_count, counts(10*1024)
INTEGER , dimension(3) :: dom_end_rev
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
LOGICAL distributed_field
INTEGER i,j,k,idx,lx,idx2,lx2
CALL wrf_get_nproc( nproc )
CALL wrf_get_dm_communicator ( communicator )
CALL lower_case( MemoryOrder, MemOrd )
dom_end_rev(1) = DomainEnd(1)
dom_end_rev(2) = DomainEnd(2)
dom_end_rev(3) = DomainEnd(3)
SELECT CASE (TRIM(MemOrd))
CASE ( 'xzy' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'zxy' )
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'xyz' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'xy' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
CASE ( 'yxz' )
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'yx' )
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
CASE DEFAULT
! do nothing; the boundary orders and others either dont care or set themselves
END SELECT
SELECT CASE (TRIM(MemOrd))
#ifndef STUBMPI
CASE ( 'xzy','zxy','xyz','yxz','xy','yx' )
distributed_field = .TRUE.
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_patch_to_global_double ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_patch_to_global_real ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_patch_to_global_integer ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
CALL wrf_patch_to_global_logical ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ENDIF
#if defined(DM_PARALLEL) && !defined(STUBMPI)
CASE ( 'xsz', 'xez' )
distributed_field = .FALSE.
IF ( nproc .GT. 1 ) THEN
jds = DomainStart(1) ; jde = DomainEnd(1) ; IF ( .NOT. has_char( Stagger, 'y' ) ) jde = jde+1 ! ns strip
kds = DomainStart(2) ; kde = DomainEnd(2) ; IF ( .NOT. has_char( Stagger, 'z' ) ) kde = kde+1 ! levels
ids = DomainStart(3) ; ide = DomainEnd(3) ; ! bdy_width
dom_end_rev(1) = jde
dom_end_rev(2) = kde
dom_end_rev(3) = ide
distributed_field = .TRUE.
IF ( (MemOrd .eq. 'xsz' .AND. bdy_mask( P_XSB )) .OR. &
(MemOrd .eq. 'xez' .AND. bdy_mask( P_XEB )) ) THEN
my_displ = PatchStart(1)-1
my_count = PatchEnd(1)-PatchStart(1)+1
ELSE
my_displ = 0
my_count = 0
ENDIF
CALL mpi_gather( my_displ, 1, MPI_INTEGER, displs, 1, MPI_INTEGER, 0, communicator, ierr )
CALL mpi_gather( my_count, 1, MPI_INTEGER, counts, 1, MPI_INTEGER, 0, communicator, ierr )
do i = DomainStart(3),DomainEnd(3) ! bdy_width
do k = DomainStart(2),DomainEnd(2) ! levels
lx = MemoryEnd(1)-MemoryStart(1)+1
lx2 = dom_end_rev(1)-DomainStart(1)+1
idx = lx*((k-1)+(i-1)*(MemoryEnd(2)-MemoryStart(2)+1))
idx2 = lx2*((k-1)+(i-1)*(MemoryEnd(2)-MemoryStart(2)+1))
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_gatherv_double ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_gatherv_real ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_gatherv_integer ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ENDIF
enddo
enddo
ENDIF
CASE ( 'xs', 'xe' )
distributed_field = .FALSE.
IF ( nproc .GT. 1 ) THEN
jds = DomainStart(1) ; jde = DomainEnd(1) ; IF ( .NOT. has_char( Stagger, 'y' ) ) jde = jde+1 ! ns strip
ids = DomainStart(2) ; ide = DomainEnd(2) ; ! bdy_width
dom_end_rev(1) = jde
dom_end_rev(2) = ide
distributed_field = .TRUE.
IF ( (MemOrd .eq. 'xs' .AND. bdy_mask( P_XSB )) .OR. &
(MemOrd .eq. 'xe' .AND. bdy_mask( P_XEB )) ) THEN
my_displ = PatchStart(1)-1
my_count = PatchEnd(1)-PatchStart(1)+1
ELSE
my_displ = 0
my_count = 0
ENDIF
CALL mpi_gather( my_displ, 1, MPI_INTEGER, displs, 1, MPI_INTEGER, 0, communicator, ierr )
CALL mpi_gather( my_count, 1, MPI_INTEGER, counts, 1, MPI_INTEGER, 0, communicator, ierr )
do i = DomainStart(2),DomainEnd(2) ! bdy_width
lx = MemoryEnd(1)-MemoryStart(1)+1
idx = lx*(i-1)
lx2 = dom_end_rev(1)-DomainStart(1)+1
idx2 = lx2*(i-1)
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_gatherv_double ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_gatherv_real ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_gatherv_integer ( Field, PatchStart(1)+idx , &
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ENDIF
enddo
ENDIF
CASE ( 'ysz', 'yez' )
distributed_field = .FALSE.
IF ( nproc .GT. 1 ) THEN
ids = DomainStart(1) ; ide = DomainEnd(1) ; IF ( .NOT. has_char( Stagger, 'y' ) ) ide = ide+1 ! ns strip
kds = DomainStart(2) ; kde = DomainEnd(2) ; IF ( .NOT. has_char( Stagger, 'z' ) ) kde = kde+1 ! levels
jds = DomainStart(3) ; jde = DomainEnd(3) ; ! bdy_width
dom_end_rev(1) = ide
dom_end_rev(2) = kde
dom_end_rev(3) = jde
distributed_field = .TRUE.
IF ( (MemOrd .eq. 'ysz' .AND. bdy_mask( P_YSB )) .OR. &
(MemOrd .eq. 'yez' .AND. bdy_mask( P_YEB )) ) THEN
my_displ = PatchStart(1)-1
my_count = PatchEnd(1)-PatchStart(1)+1
ELSE
my_displ = 0
my_count = 0
ENDIF
CALL mpi_gather( my_displ, 1, MPI_INTEGER, displs, 1, MPI_INTEGER, 0, communicator, ierr )
CALL mpi_gather( my_count, 1, MPI_INTEGER, counts, 1, MPI_INTEGER, 0, communicator, ierr )
do j = DomainStart(3),DomainEnd(3) ! bdy_width
do k = DomainStart(2),DomainEnd(2) ! levels
lx = MemoryEnd(1)-MemoryStart(1)+1
lx2 = dom_end_rev(1)-DomainStart(1)+1
idx = lx*((k-1)+(j-1)*(MemoryEnd(2)-MemoryStart(2)+1))
idx2 = lx2*((k-1)+(j-1)*(MemoryEnd(2)-MemoryStart(2)+1))
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_gatherv_double ( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_gatherv_real( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_gatherv_integer( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ENDIF
enddo
enddo
ENDIF
CASE ( 'ys', 'ye' )
distributed_field = .FALSE.
IF ( nproc .GT. 1 ) THEN
ids = DomainStart(1) ; ide = DomainEnd(1) ; IF ( .NOT. has_char( Stagger, 'y' ) ) ide = ide+1 ! ns strip
jds = DomainStart(2) ; jde = DomainEnd(2) ; ! bdy_width
dom_end_rev(1) = ide
dom_end_rev(2) = jde
distributed_field = .TRUE.
IF ( (MemOrd .eq. 'ys' .AND. bdy_mask( P_YSB )) .OR. &
(MemOrd .eq. 'ye' .AND. bdy_mask( P_YEB )) ) THEN
my_displ = PatchStart(1)-1
my_count = PatchEnd(1)-PatchStart(1)+1
ELSE
my_displ = 0
my_count = 0
ENDIF
CALL mpi_gather( my_displ, 1, MPI_INTEGER, displs, 1, MPI_INTEGER, 0, communicator, ierr )
CALL mpi_gather( my_count, 1, MPI_INTEGER, counts, 1, MPI_INTEGER, 0, communicator, ierr )
do j = DomainStart(2),DomainEnd(2) ! bdy_width
lx = MemoryEnd(1)-MemoryStart(1)+1
idx = lx*(j-1)
lx2 = dom_end_rev(1)-DomainStart(1)+1
idx2 = lx2*(j-1)
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_gatherv_double( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_gatherv_real( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_gatherv_integer( Field, PatchStart(1)+idx , & ! sendbuf
my_count , & ! sendcount
globbuf, 1+idx2 , & ! recvbuf
counts , & ! recvcounts
displs , & ! displs
0 , & ! root
communicator , & ! communicator
ierr )
ENDIF
enddo
ENDIF
#endif
#endif
CASE DEFAULT
distributed_field = .FALSE.
END SELECT
IF ( wrf_dm_on_monitor() ) THEN
IF ( distributed_field ) THEN
CALL fcn ( Hndl , DateStr , VarName , globbuf , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
DomainStart , dom_end_rev , & ! memory dims adjust out for unstag
DomainStart , DomainEnd , &
Status )
ELSE
CALL fcn ( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ENDIF
ENDIF
CALL wrf_dm_bcast_bytes( Status , IWORDSIZE )
RETURN
END SUBROUTINE collect_generic_and_call_pkg
SUBROUTINE call_pkg_and_dist ( fcn, donotdist_arg, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , bdy_mask, MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! This routine handle cases where distribution/replication can be skipped and
! deals with different data types for Field.
!
!
IMPLICIT NONE
include 'wrf_io_flags.h'
EXTERNAL fcn
LOGICAL, INTENT(IN) :: donotdist_arg, update_arg ! update means collect old field update it and dist
INTEGER , INTENT(IN) :: Hndl
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
INTEGER :: Field(*)
INTEGER :: FieldType
INTEGER :: Comm
INTEGER :: IOComm
INTEGER :: DomainDesc
LOGICAL, DIMENSION(4) :: bdy_mask
CHARACTER*(*) :: MemoryOrder
CHARACTER*(*) :: Stagger
CHARACTER*(*) , dimension (*) :: DimNames
INTEGER ,dimension(*) :: DomainStart, DomainEnd
INTEGER ,dimension(*) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) :: PatchStart, PatchEnd
INTEGER :: Status
LOGICAL donotdist
INTEGER ndims, nproc
CALL dim_from_memorder( MemoryOrder , ndims)
CALL wrf_get_nproc( nproc )
donotdist = donotdist_arg .OR. (nproc .EQ. 1)
IF ( donotdist ) THEN
CALL fcn ( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF (FieldType .EQ. WRF_DOUBLE) THEN
CALL call_pkg_and_dist_double ( fcn, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF (FieldType .EQ. WRF_REAL) THEN
CALL call_pkg_and_dist_real ( fcn, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL call_pkg_and_dist_int ( fcn, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
CALL call_pkg_and_dist_logical ( fcn, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ENDIF
RETURN
END SUBROUTINE call_pkg_and_dist
SUBROUTINE call_pkg_and_dist_real ( fcn, update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! The sole purpose of this wrapper is to allocate a big real buffer and
! pass it down to call_pkg_and_dist_generic() to do the actual work.
!
!
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
LOGICAL , INTENT(IN) :: update_arg
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
REAL , INTENT(INOUT) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
REAL, ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
globbuf = 0.
CALL call_pkg_and_dist_generic ( fcn, globbuf FRSTELEM , update_arg, &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE call_pkg_and_dist_real
SUBROUTINE call_pkg_and_dist_double ( fcn, update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! The sole purpose of this wrapper is to allocate a big double precision buffer
! and pass it down to call_pkg_and_dist_generic() to do the actual work.
!
!
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
LOGICAL , INTENT(IN) :: update_arg
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
DOUBLE PRECISION , INTENT(INOUT) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
DOUBLE PRECISION , ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
globbuf = 0
CALL call_pkg_and_dist_generic ( fcn, globbuf FRSTELEM , update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE call_pkg_and_dist_double
SUBROUTINE call_pkg_and_dist_int ( fcn, update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! The sole purpose of this wrapper is to allocate a big integer buffer and
! pass it down to call_pkg_and_dist_generic() to do the actual work.
!
!
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
LOGICAL , INTENT(IN) :: update_arg
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
INTEGER , INTENT(INOUT) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
INTEGER , ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
globbuf = 0
CALL call_pkg_and_dist_generic ( fcn, globbuf FRSTELEM , update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE call_pkg_and_dist_int
SUBROUTINE call_pkg_and_dist_logical ( fcn, update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! The sole purpose of this wrapper is to allocate a big logical buffer and
! pass it down to call_pkg_and_dist_generic() to do the actual work.
!
!
IMPLICIT NONE
EXTERNAL fcn
INTEGER , INTENT(IN) :: Hndl
LOGICAL , INTENT(IN) :: update_arg
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
logical , INTENT(INOUT) :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(INOUT) :: Status
LOGICAL , ALLOCATABLE :: globbuf (:)
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
IF ( wrf_dm_on_monitor() ) THEN
ALLOCATE( globbuf( (DomainEnd(1)-DomainStart(1)+3)*(DomainEnd(2)-DomainStart(2)+3)*(DomainEnd(3)-DomainStart(3)+3) ) )
ELSE
ALLOCATE( globbuf( 1 ) )
ENDIF
globbuf = .false.
CALL call_pkg_and_dist_generic ( fcn, globbuf FRSTELEM , update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
DEALLOCATE ( globbuf )
RETURN
END SUBROUTINE call_pkg_and_dist_logical
SUBROUTINE call_pkg_and_dist_generic ( fcn, globbuf , update_arg , &
Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
!
!
! The call_pkg_and_dist* routines call an I/O function to read a field and then
! distribute or replicate the field across compute tasks.
! This routine calls I/O function fcn to read the field from disk and then calls
! the distributed memory communication routines that distribute or replicate the
! array.
!
!
USE module_driver_constants
IMPLICIT NONE
#include
EXTERNAL fcn
REAL, DIMENSION(*) :: globbuf
INTEGER , INTENT(IN) :: Hndl
LOGICAL , INTENT(IN) :: update_arg
CHARACTER*(*) :: DateStr
CHARACTER*(*) :: VarName
REAL :: Field(*)
INTEGER ,INTENT(IN) :: FieldType
INTEGER ,INTENT(INOUT) :: Comm
INTEGER ,INTENT(INOUT) :: IOComm
INTEGER ,INTENT(IN) :: DomainDesc
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(IN) :: Stagger
CHARACTER*(*) , dimension (*) ,INTENT(IN) :: DimNames
INTEGER ,dimension(*) ,INTENT(IN) :: DomainStart, DomainEnd
INTEGER ,dimension(*) ,INTENT(IN) :: MemoryStart, MemoryEnd
INTEGER ,dimension(*) ,INTENT(IN) :: PatchStart, PatchEnd
INTEGER ,INTENT(OUT) :: Status
CHARACTER*3 MemOrd
LOGICAL, EXTERNAL :: has_char
INTEGER ids, ide, jds, jde, kds, kde
INTEGER ims, ime, jms, jme, kms, kme
INTEGER ips, ipe, jps, jpe, kps, kpe
INTEGER , dimension(3) :: dom_end_rev
INTEGER memsize
LOGICAL, EXTERNAL :: wrf_dm_on_monitor
LOGICAL distributed_field
CALL lower_case( MemoryOrder, MemOrd )
dom_end_rev(1) = DomainEnd(1)
dom_end_rev(2) = DomainEnd(2)
dom_end_rev(3) = DomainEnd(3)
SELECT CASE (TRIM(MemOrd))
CASE ( 'xzy' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'zxy' )
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'xyz' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'xy' )
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
CASE ( 'yxz' )
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
IF ( .NOT. has_char( Stagger, 'z' ) ) dom_end_rev(3) = dom_end_rev(3) + 1
CASE ( 'yx' )
IF ( .NOT. has_char( Stagger, 'y' ) ) dom_end_rev(1) = dom_end_rev(1) + 1
IF ( .NOT. has_char( Stagger, 'x' ) ) dom_end_rev(2) = dom_end_rev(2) + 1
CASE DEFAULT
! do nothing; the boundary orders and others either dont care or set themselves
END SELECT
SELECT CASE (MemOrd)
CASE ( 'xzy' )
distributed_field = .TRUE.
CASE ( 'xyz' )
distributed_field = .TRUE.
CASE ( 'yxz' )
distributed_field = .TRUE.
CASE ( 'zxy' )
distributed_field = .TRUE.
CASE ( 'xy' )
distributed_field = .TRUE.
CASE ( 'yx' )
distributed_field = .TRUE.
CASE DEFAULT
! all other memory orders are replicated
distributed_field = .FALSE.
END SELECT
IF ( distributed_field ) THEN
! added 8/2004 for interfaces, like MCEL, that want the old values so they can be updated
IF ( update_arg ) THEN
SELECT CASE (TRIM(MemOrd))
CASE ( 'xzy','zxy','xyz','yxz','xy','yx' )
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_patch_to_global_double ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
CALL wrf_patch_to_global_real ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_patch_to_global_integer ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
CALL wrf_patch_to_global_logical ( Field , globbuf , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
ENDIF
CASE DEFAULT
END SELECT
ENDIF
IF ( wrf_dm_on_monitor()) THEN
CALL fcn ( Hndl , DateStr , VarName , globbuf , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
DomainStart , dom_end_rev , &
DomainStart , DomainEnd , &
Status )
ENDIF
CALL wrf_dm_bcast_bytes( Status, IWORDSIZE )
CALL lower_case( MemoryOrder, MemOrd )
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
SELECT CASE (MemOrd)
CASE ( 'xzy','xyz','yxz','zxy' )
CALL wrf_global_to_patch_double ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
CASE ( 'xy','yx' )
CALL wrf_global_to_patch_double ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), 1 , 1 , &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), 1 , 1 , &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , 1 , 1 )
END SELECT
ELSE IF ( FieldType .EQ. WRF_REAL ) THEN
SELECT CASE (MemOrd)
CASE ( 'xzy','xyz','yxz','zxy' )
CALL wrf_global_to_patch_real ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
CASE ( 'xy','yx' )
CALL wrf_global_to_patch_real ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), 1 , 1 , &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), 1 , 1 , &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , 1 , 1 )
END SELECT
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
SELECT CASE (MemOrd)
CASE ( 'xzy','xyz','yxz','zxy' )
CALL wrf_global_to_patch_integer ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
CASE ( 'xy','yx' )
CALL wrf_global_to_patch_integer ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), 1 , 1 , &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), 1 , 1 , &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , 1 , 1 )
END SELECT
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
SELECT CASE (MemOrd)
CASE ( 'xzy','xyz','yxz','zxy' )
CALL wrf_global_to_patch_logical ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), DomainStart(3), DomainEnd(3), &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), MemoryStart(3), MemoryEnd(3), &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , PatchStart(3) , PatchEnd(3) )
CASE ( 'xy','yx' )
CALL wrf_global_to_patch_logical ( globbuf, Field , DomainDesc, Stagger, MemOrd , &
DomainStart(1), DomainEnd(1), DomainStart(2), DomainEnd(2), 1 , 1 , &
MemoryStart(1), MemoryEnd(1), MemoryStart(2), MemoryEnd(2), 1 , 1 , &
PatchStart(1) , PatchEnd(1) , PatchStart(2) , PatchEnd(2) , 1 , 1 )
END SELECT
ENDIF
ELSE
IF ( wrf_dm_on_monitor()) THEN
CALL fcn ( Hndl , DateStr , VarName , Field , FieldType , Comm , IOComm , &
DomainDesc , MemoryOrder , Stagger , DimNames , &
DomainStart , DomainEnd , &
MemoryStart , MemoryEnd , &
PatchStart , PatchEnd , &
Status )
ENDIF
CALL wrf_dm_bcast_bytes( Status, IWORDSIZE )
memsize = (MemoryEnd(1)-MemoryStart(1)+1)*(MemoryEnd(2)-MemoryStart(2)+1)*(MemoryEnd(3)-MemoryStart(3)+1)
IF ( FieldType .EQ. WRF_DOUBLE ) THEN
CALL wrf_dm_bcast_bytes( Field , DWORDSIZE*memsize )
ELSE IF ( FieldType .EQ. WRF_REAL) THEN
CALL wrf_dm_bcast_bytes( Field , RWORDSIZE*memsize )
ELSE IF ( FieldType .EQ. WRF_INTEGER ) THEN
CALL wrf_dm_bcast_bytes( Field , IWORDSIZE*memsize )
ELSE IF ( FieldType .EQ. WRF_LOGICAL ) THEN
CALL wrf_dm_bcast_bytes( Field , LWORDSIZE*memsize )
ENDIF
ENDIF
RETURN
END SUBROUTINE call_pkg_and_dist_generic
!!!!!! Miscellaneous routines
! stole these routines from io_netcdf external package; changed names to avoid collisions
SUBROUTINE dim_from_memorder(MemoryOrder,NDim)
!
!
! Decodes array ranks from memory order.
!
!
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
INTEGER ,INTENT(OUT) :: NDim
!Local
CHARACTER*3 :: MemOrd
!
CALL Lower_Case(MemoryOrder,MemOrd)
SELECT CASE (MemOrd)
CASE ('xyz','xzy','yxz','yzx','zxy','zyx')
NDim = 3
CASE ('xy','yx')
NDim = 2
CASE ('z','c','0')
NDim = 1
CASE DEFAULT
NDim = 0
RETURN
END SELECT
RETURN
END SUBROUTINE dim_from_memorder
SUBROUTINE lower_case(MemoryOrder,MemOrd)
!
!
! Translates upper-case characters to lower-case.
!
!
CHARACTER*(*) ,INTENT(IN) :: MemoryOrder
CHARACTER*(*) ,INTENT(OUT) :: MemOrd
!Local
CHARACTER*1 :: c
INTEGER ,PARAMETER :: upper_to_lower =IACHAR('a')-IACHAR('A')
INTEGER :: i,n
!
MemOrd = ' '
N = len(MemoryOrder)
MemOrd(1:N) = MemoryOrder(1:N)
DO i=1,N
c = MemoryOrder(i:i)
if('A'<=c .and. c <='Z') MemOrd(i:i)=achar(iachar(c)+upper_to_lower)
ENDDO
RETURN
END SUBROUTINE Lower_Case
LOGICAL FUNCTION has_char( str, c )
!
!
! Returns .TRUE. iff string str contains character c. Ignores character case.
!
!
IMPLICIT NONE
CHARACTER*(*) str
CHARACTER c, d
CHARACTER*80 str1, str2, str3
INTEGER i
CALL lower_case( TRIM(str), str1 )
str2 = ""
str2(1:1) = c
CALL lower_case( str2, str3 )
d = str3(1:1)
DO i = 1, LEN(TRIM(str1))
IF ( str1(i:i) .EQ. d ) THEN
has_char = .TRUE.
RETURN
ENDIF
ENDDO
has_char = .FALSE.
RETURN
END FUNCTION has_char