! $Id$
!
! Earth System Modeling Framework
! Copyright (c) 2002-2025, University Corporation for Atmospheric Research, 
! Massachusetts Institute of Technology, Geophysical Fluid Dynamics 
! Laboratory, University of Michigan, National Centers for Environmental 
! Prediction, Los Alamos National Laboratory, Argonne National Laboratory, 
! NASA Goddard Space Flight Center.
! Licensed under the University of Illinois-NCSA License.
!
!==============================================================================
#define ESMF_FILENAME "ESMF_FieldGetAllocBounds.F90"
!==============================================================================
!
!     ESMF FieldGetAllocBounds module
module ESMF_FieldGetAllocBoundsMod
!
!==============================================================================
!
! This file contains the Field Get Allocation Bounds methods.
!
!------------------------------------------------------------------------------
! INCLUDES
#include "ESMF.h"

!------------------------------------------------------------------------------
!
!BOPI
! !MODULE: ESMF_FieldGetAllocBoundsMod - Get Field allocation bounds
!
! !DESCRIPTION:
! The code in this file implements the {\tt ESMF\_Field} allocation bounds 
! computation methods.
!
!------------------------------------------------------------------------------
! !USES:
  use ESMF_UtilTypesMod
  use ESMF_BaseMod
  use ESMF_LogErrMod
  use ESMF_ArraySpecMod
  use ESMF_LocalArrayMod
  use ESMF_ArrayMod
  use ESMF_ArrayGetMod
  use ESMF_GridMod
  use ESMF_XGridMod
  use ESMF_MeshMod
  use ESMF_LocStreamMod
  use ESMF_GeomMod
  use ESMF_StaggerLocMod
  use ESMF_InitMacrosMod
  
  use ESMF_FieldMod

  implicit none

!------------------------------------------------------------------------------
! !PRIVATE TYPES:
  private

!------------------------------------------------------------------------------
!
! !PUBLIC MEMBER FUNCTIONS:
!
! - ESMF-public methods:
   public ESMF_GridGetFieldBounds
   public ESMF_MeshGetFieldBounds
   public ESMF_LocStreamGetFieldBounds
   public ESMF_XGridGetFieldBounds
!
!EOPI

!------------------------------------------------------------------------------
! The following line turns the CVS identifier string into a printable variable.
  character(*), parameter, private :: version = &
    '$Id$'

!==============================================================================
!
! INTERFACE BLOCKS
!
!==============================================================================

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

contains

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

!------------------------------------------------------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_GridGetFieldBounds"
!BOP
! !IROUTINE: ESMF_GridGetFieldBounds -  Get precomputed DE-local Fortran data array bounds for creating a Field from a Grid and Fortran array

! !INTERFACE:
    subroutine ESMF_GridGetFieldBounds(grid, keywordEnforcer, &
        localDe, staggerloc, gridToFieldMap, &
        ungriddedLBound, ungriddedUBound, &
        totalLWidth, totalUWidth, &
        totalLBound, totalUBound, totalCount, rc)
    
! !ARGUMENTS:
    type(ESMF_Grid),       intent(in)            :: grid     
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,               intent(in),  optional :: localDe
    type(ESMF_StaggerLoc), intent(in),  optional :: staggerloc 
    integer,               intent(in),  optional :: gridToFieldMap(:)    
    integer,               intent(in),  optional :: ungriddedLBound(:)
    integer,               intent(in),  optional :: ungriddedUBound(:)
    integer,               intent(in),  optional :: totalLWidth(:)
    integer,               intent(in),  optional :: totalUWidth(:)
    integer,               intent(out), optional :: totalLBound(:)
    integer,               intent(out), optional :: totalUBound(:)
    integer,               intent(out), optional :: totalCount(:)
    integer,               intent(out), optional :: rc     

!
! !STATUS:
! \begin{itemize}
! \item\apiStatusCompatibleVersion{5.2.0r}
! \end{itemize}
!
! !DESCRIPTION:
! Compute the lower and upper bounds of Fortran data array that can later
! be used in FieldCreate interface to create a {\tt ESMF\_Field} from a
! {\tt ESMF\_Grid} and the Fortran data array. For an example and
! associated documentation using this method see section 
! \ref{sec:field:usage:create_5dgrid_7dptr_2dungridded}.
!
! The arguments are:
! \begin{description}
! \item [grid]
!       {\tt ESMF\_Grid}.
! \item [{[localDe]}]
!       Local DE for which information is requested. {\tt [0,..,localDeCount-1]}.
!       For {\tt localDeCount==1} the {\tt localDe} argument may be omitted,
!       in which case it will default to {\tt localDe=0}.
! \item [{[staggerloc]}]
!       Stagger location of data in grid cells.  For valid
!       predefined values and interpretation of results see
!       section \ref{const:staggerloc}.
! \item [{[gridToFieldMap]}]
!       List with number of elements equal to the
!       {\tt grid}|s dimCount.  The list elements map each dimension
!       of the {\tt grid} to a dimension in the {\tt field} by
!       specifying the appropriate {\tt field} dimension index. The default is to
!       map all of the {\tt grid}|s dimensions against the lowest dimensions of
!       the {\tt field} in sequence, i.e. {\tt gridToFieldMap} = (/1,2,3,.../).
!       The values of all {\tt gridToFieldMap} entries must be greater than or equal
!       to one and smaller than or equal to the {\tt field} rank.
!       It is erroneous to specify the same {\tt gridToFieldMap} entry
!       multiple times. The total ungridded dimensions in the {\tt field}
!       are the total {\tt field} dimensions less
!       the dimensions in
!       the {\tt grid}.  Ungridded dimensions must be in the same order they are
!       stored in the {\tt field}.  
! \item [{[ungriddedLBound]}]
!       Lower bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedLBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[ungriddedUBound]}]
!       Upper bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedUBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[totalLWidth]}]
!       Lower bound of halo region.  The size of this array is the number
!       of dimensions in the {\tt grid}.  However, ordering of the elements
!       needs to be the same as they appear in the {\tt field}.  Values default
!       to 0.  If values for totalLWidth are specified they must be reflected in
!       the size of the {\tt field}.  That is, for each gridded dimension the
!       {\tt field} size should be max( {\tt totalLWidth} + {\tt totalUWidth}
!       + {\tt computationalCount}, {\tt exclusiveCount} ).
! \item [{[totalUWidth]}]
!       Upper bound of halo region.  The size of this array is the number
!       of dimensions in the {\tt grid}.  However, ordering of the elements
!       needs to be the same as they appear in the {\tt field}.  Values default
!       to 0.  If values for totalUWidth are specified they must be reflected in
!       the size of the {\tt field}.  That is, for each gridded dimension the
!       {\tt field} size should max( {\tt totalLWidth} + {\tt totalUWidth}
!       + {\tt computationalCount}, {\tt exclusiveCount} ).
! \item [{[totalLBound]}]
!       \begin{sloppypar}
!       The relative lower bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
!       The relative lower bounds of Fortran data array to be used
! \item [{[totalUBound]}]
!       \begin{sloppypar}
!       The relative upper bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalCount]}]
!       Number of elements need to be allocated for Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!
! \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOP

!  !Local Variables
    integer :: localrc
    type(ESMF_STAGGERLOC)                  :: l_staggerloc
    type(ESMF_Geom) :: geom

    ! Initialize
    localrc = ESMF_RC_NOT_IMPL
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ESMF_INIT_CHECK_DEEP(ESMF_GridGetInit,grid,rc)

    ! default staggerloc setup
    if(present(staggerloc)) then
        l_staggerloc = staggerloc
    else
        l_staggerloc = ESMF_STAGGERLOC_CENTER
    endif

     ! Create Geom from Grid
    geom=ESMF_GeomCreate(grid,l_staggerloc, rc=localrc)
    if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
      ESMF_CONTEXT, rcToReturn=rc)) return
 

    ! call into generic alloc bound calculation subroutine
    call ESMF_FieldGetGBAllocBounds(geom, localDe=localDe, &
        gridToFieldMap=gridToFieldMap, &
        ungriddedLBound=ungriddedLBound, ungriddedUBound=ungriddedUBound, &
        totalLWidth=totalLWidth, totalUWidth=totalUWidth, &
        totalLBound=totalLBound, totalUBound=totalUBound, &
        totalCount=totalCount, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! Destroy Geom
    call ESMF_GeomDestroy(geom, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    if (present(rc)) rc = ESMF_SUCCESS
    end subroutine ESMF_GridGetFieldBounds

!------------------------------------------------------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_LocStreamGetFieldBounds"
!BOP
! !IROUTINE: ESMF_LocStreamGetFieldBounds -  Get precomputed DE-local Fortran data array bounds for creating a Field from a LocStream and Fortran array

! !INTERFACE:
    subroutine ESMF_LocStreamGetFieldBounds(locstream, keywordEnforcer, &
        localDe, gridToFieldMap, &
        ungriddedLBound, ungriddedUBound, &
        totalLBound, totalUBound, totalCount, rc)
    
! !ARGUMENTS:
    type(ESMF_LocStream), intent(in)            :: locstream     
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    integer,              intent(in),  optional :: localDe
    integer,              intent(in),  optional :: gridToFieldMap(:)    
    integer,              intent(in),  optional :: ungriddedLBound(:)
    integer,              intent(in),  optional :: ungriddedUBound(:)
    integer,              intent(out), optional :: totalLBound(:)
    integer,              intent(out), optional :: totalUBound(:)
    integer,              intent(out), optional :: totalCount(:)
    integer,              intent(out), optional :: rc     

!
! !DESCRIPTION:
! Compute the lower and upper bounds of Fortran data array that can later
! be used in FieldCreate interface to create a {\tt ESMF\_Field} from a
! {\tt ESMF\_LocStream} and the Fortran data array.  For an example and
! associated documentation using this method see section 
! \ref{sec:field:usage:create_5dgrid_7dptr_2dungridded}.
!
! The arguments are:
! \begin{description}
! \item [locstream]
!       {\tt ESMF\_LocStream}.
! \item [{[localDe]}]
!       Local DE for which information is requested. {\tt [0,..,localDeCount-1]}.
!       For {\tt localDeCount==1} the {\tt localDe} argument may be omitted,
!       in which case it will default to {\tt localDe=0}.
! \item [{[gridToFieldMap]}]
!       List with number of elements equal to 1.
!       The list elements map the dimension
!       of the {\tt locstream} to a dimension in the {\tt field} by
!       specifying the appropriate {\tt field} dimension index. The default is to
!       map the {\tt locstream}|s dimension against the lowest dimension of
!       the {\tt field} in sequence, i.e. {\tt gridToFieldMap} = (/1/).
!       The values of all {\tt gridToFieldMap} entries must be greater than or equal
!       to one and smaller than or equal to the {\tt field} rank.
!       The total ungridded dimensions in the {\tt field}
!       are the total {\tt field} dimensions less
!       the dimensions in
!       the {\tt grid}.  Ungridded dimensions must be in the same order they are
!       stored in the {\t field}.  
! \item [{[ungriddedLBound]}]
!       Lower bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedLBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than 1, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[ungriddedUBound]}]
!       Upper bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedUBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than 1, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[totalLBound]}]
!       \begin{sloppypar}
!       The relative lower bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalUBound]}]
!       \begin{sloppypar}
!       The relative upper bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalCount]}]
!       Number of elements need to be allocated for Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!
! \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOP

!  !Local Variables
    integer :: localrc
    type(ESMF_Geom) :: geom

    ! Initialize
    localrc = ESMF_RC_NOT_IMPL
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ESMF_INIT_CHECK_DEEP(ESMF_LocStreamGetInit,locstream,rc)

     ! Create Geom from LocStream
    geom=ESMF_GeomCreate(locstream,rc=localrc)
    if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
      ESMF_CONTEXT, rcToReturn=rc)) return
 

    ! call into generic alloc bound calculation subroutine
    call ESMF_FieldGetGBAllocBounds(geom, localDe=localDe, &
        gridToFieldMap=gridToFieldMap, &
        ungriddedLBound=ungriddedLBound, ungriddedUBound=ungriddedUBound, &
        totalLBound=totalLBound, totalUBound=totalUBound, &
        totalCount=totalCount, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! Destroy Geom
    call ESMF_GeomDestroy(geom, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    if (present(rc)) rc = ESMF_SUCCESS
    end subroutine ESMF_LocStreamGetFieldBounds


!------------------------------------------------------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_MeshGetFieldBounds"
!BOP
! !IROUTINE: ESMF_MeshGetFieldBounds -  Get precomputed DE-local Fortran data array bounds for creating a Field from a Mesh and a Fortran array

! !INTERFACE:
    subroutine ESMF_MeshGetFieldBounds(mesh, keywordEnforcer, &
        meshloc, &
        localDe, gridToFieldMap, &
        ungriddedLBound, ungriddedUBound, &
        totalLBound, totalUBound, totalCount, rc)
    
! !ARGUMENTS:
    type(ESMF_Mesh), intent(in)            :: mesh     
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    type(ESMF_MeshLoc),intent(in),optional :: meshloc
    integer,         intent(in),  optional :: localDe
    integer,         intent(in),  optional :: gridToFieldMap(:)    
    integer,         intent(in),  optional :: ungriddedLBound(:)
    integer,         intent(in),  optional :: ungriddedUBound(:)
    integer,         intent(out), optional :: totalLBound(:)
    integer,         intent(out), optional :: totalUBound(:)
    integer,         intent(out), optional :: totalCount(:)
    integer,         intent(out), optional :: rc     

!
! !DESCRIPTION:
! Compute the lower and upper bounds of Fortran data array that can later
! be used in FieldCreate interface to create a {\tt ESMF\_Field} from a
! {\tt ESMF\_Mesh} and the Fortran data array. For an example and
! associated documentation using this method see section 
! \ref{sec:field:usage:create_5dgrid_7dptr_2dungridded}.
!
! The arguments are:
! \begin{description}
! \item [mesh]
!       {\tt ESMF\_Mesh}.
! \item [{[meshloc]}]
!       \begin{sloppypar}
!       Which part of the mesh to build the Field on. Can be set to either
!       {\tt ESMF\_MESHLOC\_NODE} or {\tt ESMF\_MESHLOC\_ELEMENT}. If not set,
!       defaults to {\tt ESMF\_MESHLOC\_NODE}.
!       \end{sloppypar}
! \item [{[localDe]}]
!       Local DE for which information is requested. {\tt [0,..,localDeCount-1]}.
!       For {\tt localDeCount==1} the {\tt localDe} argument may be omitted,
!       in which case it will default to {\tt localDe=0}.
! \item [{[gridToFieldMap]}]
!       List with number of elements equal to the
!       {\tt grid}|s dimCount.  The list elements map each dimension
!       of the {\tt grid} to a dimension in the {\tt field} by
!       specifying the appropriate {\tt field} dimension index. The default is to
!       map all of the {\tt grid}|s dimensions against the lowest dimensions of
!       the {\tt field} in sequence, i.e. {\tt gridToFieldMap} = (/1,2,3,.../).
!       The values of all {\tt gridToFieldMap} entries must be greater than or equal
!       to one and smaller than or equal to the {\tt field} rank.
!       It is erroneous to specify the same {\tt gridToFieldMap} entry
!       multiple times. The total ungridded dimensions in the {\tt field}
!       are the total {\tt field} dimensions less
!       the dimensions in
!       the {\tt grid}.  Ungridded dimensions must be in the same order they are
!       stored in the {\t field}.  
! \item [{[ungriddedLBound]}]
!       Lower bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedLBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[ungriddedUBound]}]
!       Upper bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedUBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[totalLBound]}]
!       \begin{sloppypar}
!       The relative lower bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Mesh} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalUBound]}]
!       \begin{sloppypar}
!       The relative upper bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Mesh} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalCount]}]
!       Number of elements need to be allocated for Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Mesh} and Fortran data array.
!       This is an output variable from this user interface.
!
! \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOP

!  !Local Variables
    integer :: localrc
    type(ESMF_Geom) :: geom

    ! Initialize
    localrc = ESMF_RC_NOT_IMPL
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ESMF_INIT_CHECK_DEEP(ESMF_MeshGetInit,mesh,rc)

     ! Create Geom from Mesh
    geom=ESMF_GeomCreate(mesh, meshLoc=meshloc, rc=localrc)
    if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
      ESMF_CONTEXT, rcToReturn=rc)) return
 

    ! call into generic alloc bound calculation subroutine
    call ESMF_FieldGetGBAllocBounds(geom, localDe=localDe, &
        gridToFieldMap=gridToFieldMap, &
        ungriddedLBound=ungriddedLBound, ungriddedUBound=ungriddedUBound, &
        totalLBound=totalLBound, totalUBound=totalUBound, &
        totalCount=totalCount, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! Destroy Geom
    call ESMF_GeomDestroy(geom, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    if (present(rc)) rc = ESMF_SUCCESS
    end subroutine ESMF_MeshGetFieldBounds

!------------------------------------------------------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_XGridGetFieldBounds"
!BOP
! !IROUTINE: ESMF_XGridGetFieldBounds -  Get precomputed DE-local Fortran data array bounds for creating a Field from an XGrid and a Fortran array

! !INTERFACE:
    subroutine ESMF_XGridGetFieldBounds(xgrid, keywordEnforcer, &
        xgridside, gridindex, localDe, gridToFieldMap, &
        ungriddedLBound, ungriddedUBound, &
        totalLBound, totalUBound, totalCount, rc)
    
! !ARGUMENTS:
    type(ESMF_XGrid),          intent(in)            :: xgrid
type(ESMF_KeywordEnforcer), optional:: keywordEnforcer ! must use keywords below
    type(ESMF_XGridSide_Flag), intent(in),  optional :: xgridside
    integer,                   intent(in),  optional :: gridindex
    integer,                   intent(in),  optional :: localDe
    integer,                   intent(in),  optional :: gridToFieldMap(:)    
    integer,                   intent(in),  optional :: ungriddedLBound(:)
    integer,                   intent(in),  optional :: ungriddedUBound(:)
    integer,                   intent(out), optional :: totalLBound(:)
    integer,                   intent(out), optional :: totalUBound(:)
    integer,                   intent(out), optional :: totalCount(:)
    integer,                   intent(out), optional :: rc     

!
! !DESCRIPTION:
! Compute the lower and upper bounds of Fortran data array that can later
! be used in FieldCreate interface to create a {\tt ESMF\_Field} from a
! {\tt ESMF\_XGrid} and the Fortran data array.  For an example and
! associated documentation using this method see section 
! \ref{sec:field:usage:create_5dgrid_7dptr_2dungridded}.
!
! The arguments are:
! \begin{description}
! \item [xgrid]
!        {\tt ESMF\_XGrid} object.
! \item [{[xgridside]}]
!       Which side of the XGrid to create the Field on (either ESMF\_XGRIDSIDE\_A,
!       ESMF\_XGRIDSIDE\_B, or ESMF\_XGRIDSIDE\_BALANCED). If not passed in then
!       defaults to ESMF\_XGRIDSIDE\_BALANCED.
! \item [{[gridindex]}]
!       If xgridside is ESMF\_XGRIDSIDE\_A or ESMF\_XGRIDSIDE\_B then this index tells which Grid on
!       that side to create the Field on. If not provided, defaults to 1.
! \item [{[localDe]}]
!       Local DE for which information is requested. {\tt [0,..,localDeCount-1]}.
!       For {\tt localDeCount==1} the {\tt localDe} argument may be omitted,
!       in which case it will default to {\tt localDe=0}.
! \item [{[gridToFieldMap]}]
!       List with number of elements equal to 1.
!       The list elements map the dimension
!       of the {\tt locstream} to a dimension in the {\tt field} by
!       specifying the appropriate {\tt field} dimension index. The default is to
!       map the {\tt locstream}|s dimension against the lowest dimension of
!       the {\tt field} in sequence, i.e. {\tt gridToFieldMap} = (/1/).
!       The values of all {\tt gridToFieldMap} entries must be greater than or equal
!       to one and smaller than or equal to the {\tt field} rank.
!       The total ungridded dimensions in the {\tt field}
!       are the total {\tt field} dimensions less
!       the dimensions in
!       the {\tt grid}.  Ungridded dimensions must be in the same order they are
!       stored in the {\t field}.  
! \item [{[ungriddedLBound]}]
!       Lower bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedLBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than 1, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[ungriddedUBound]}]
!       Upper bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedUBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than 1, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[totalLBound]}]
!       \begin{sloppypar}
!       The relative lower bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalUBound]}]
!       \begin{sloppypar}
!       The relative upper bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalCount]}]
!       Number of elements need to be allocated for Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_LocStream} and Fortran data array.
!       This is an output variable from this user interface.
!
! \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOP

!  !Local Variables
    integer :: localrc
    type(ESMF_Geom) :: geom

    ! Initialize
    localrc = ESMF_RC_NOT_IMPL
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ESMF_INIT_CHECK_DEEP(ESMF_XGridGetInit, xgrid, rc)

     ! Create Geom from LocStream
    geom=ESMF_GeomCreate(xgrid, xgridSide, gridIndex, rc=localrc)
    if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, &
      ESMF_CONTEXT, rcToReturn=rc)) return
 

    ! call into generic alloc bound calculation subroutine
    call ESMF_FieldGetGBAllocBounds(geom, localDe=localDe, &
        gridToFieldMap=gridToFieldMap, &
        ungriddedLBound=ungriddedLBound, ungriddedUBound=ungriddedUBound, &
        totalLBound=totalLBound, totalUBound=totalUBound, &
        totalCount=totalCount, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! Destroy Geom
    call ESMF_GeomDestroy(geom, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    if (present(rc)) rc = ESMF_SUCCESS
    end subroutine ESMF_XGridGetFieldBounds

!------------------------------------------------------------------------------
#undef  ESMF_METHOD
#define ESMF_METHOD "ESMF_FieldGetGBAllocBounds"
!BOPI
! !IROUTINE: ESMF_FieldGet -  Get precomputed Fortran data array bounds

! !INTERFACE:
  ! Private name; call using ESMF_FieldGet()
    subroutine ESMF_FieldGetGBAllocBounds(geom, &
        localDe, gridToFieldMap, &
        ungriddedLBound, ungriddedUBound, &
        totalLWidth, totalUWidth, &
        totalLBound, totalUBound, totalCount, rc)
    
! !ARGUMENTS:
    type(ESMF_Geom), intent(inout)         :: geom     
    integer,             intent(in),  optional :: localDe
    integer,             intent(in),  optional :: gridToFieldMap(:)    
    integer,             intent(in),  optional :: ungriddedLBound(:)
    integer,             intent(in),  optional :: ungriddedUBound(:)
    integer,             intent(in),  optional :: totalLWidth(:)
    integer,             intent(in),  optional :: totalUWidth(:)
    integer,             intent(out), optional :: totalLBound(:)
    integer,             intent(out), optional :: totalUBound(:)
    integer,             intent(out), optional :: totalCount(:)
    integer,             intent(out), optional :: rc     

!
! !DESCRIPTION:
! Compute the lower and upper bounds of Fortran data array that can later
! be used in FieldCreate interface to create a {\tt ESMF\_Field} from a
! {\tt ESMF\_Grid} and the Fortran data array. For an example and
! associated documentation using this method see section 
! \ref{sec:field:usage:create_5dgrid_7dptr_2dungridded}.
!
! The arguments are:
! \begin{description}
! \item [geom]
!       {\tt ESMF\_Geom}.
! \item [localDe]
!       The local DE number in its PET context to compute the bounds and counts
!       information based on the computational and exclusive bounds and counts 
!       information of the grid from that local DE in its PET context.
! \item [{[gridToFieldMap]}]
!       List with number of elements equal to the
!       {\tt grid}|s dimCount.  The list elements map each dimension
!       of the {\tt grid} to a dimension in the {\tt field} by
!       specifying the appropriate {\tt field} dimension index. The default is to
!       map all of the {\tt grid}|s dimensions against the lowest dimensions of
!       the {\tt field} in sequence, i.e. {\tt gridToFieldMap} = (/1,2,3,.../).
!       The total ungridded dimensions in the {\tt field}
!       are the total {\tt field} dimensions less
!       the dimensions in
!       the {\tt grid}.  Ungridded dimensions must be in the same order they are
!       stored in the {\t field}.  
! \item [{[ungriddedLBound]}]
!       Lower bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedLBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[ungriddedUBound]}]
!       Upper bounds of the ungridded dimensions of the {\tt field}.
!       The number of elements in the {\tt ungriddedUBound} is equal to the number of ungridded
!       dimensions in the {\tt field}.  All ungridded dimensions of the
!       {\tt field} are also undistributed. When field dimension count is
!       greater than grid dimension count, both ungriddedLBound and ungriddedUBound
!       must be specified. When both are specified the values are checked
!       for consistency.  Note that the the ordering of
!       these ungridded dimensions is the same as their order in the {\tt field}.
! \item [{[totalLWidth]}]
!       Lower bound of halo region.  The size of this array is the number
!       of gridded dimensions in the {\tt field}.  However, ordering of the elements
!       needs to be the same as they appear in the {\tt field}.  Values default
!       to 0.  If values for totalLWidth are specified they must be reflected in
!       the size of the {\tt field}.  That is, for each gridded dimension the
!       {\tt field} size should be max( {\tt totalLWidth} + {\tt totalUWidth}
!       + {\tt computationalCount}, {\tt exclusiveCount} ).
! \item [{[totalUWidth]}]
!       Upper bound of halo region.  The size of this array is the number
!       of gridded dimensions in the {\tt field}.  However, ordering of the elements
!       needs to be the same as they appear in the {\tt field}.  Values default
!       to 0.  If values for totalUWidth are specified they must be reflected in
!       the size of the {\tt field}.  That is, for each gridded dimension the
!       {\tt field} size should max( {\tt totalLWidth} + {\tt totalUWidth}
!       + {\tt computationalCount}, {\tt exclusiveCount} ).
! \item [{[totalLBound]}]
!       \begin{sloppypar}
!       The relative lower bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalUBound]}]
!       \begin{sloppypar}
!       The relative upper bounds of Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!       \end{sloppypar}
! \item [{[totalCount]}]
!       Number of elements need to be allocated for Fortran data array to be used
!       later in {\tt ESMF\_FieldCreate} from {\tt ESMF\_Grid} and Fortran data array.
!       This is an output variable from this user interface.
!
! \item[{[rc]}]
!     Return code; equals {\tt ESMF\_SUCCESS} if there are no errors.
! \end{description}
!EOPI

!  !Local Variables
    integer :: localrc

!   temporary local variables corresponding to input/output arguments
    integer, dimension(ESMF_MAXDIM)        :: l_g2fm, l_mhlw, l_mhuw
    integer, dimension(:), allocatable     :: l_uglb, l_ugub
    integer, dimension(:), allocatable     :: l_alb, l_aub, l_ac

!   internal local variables 
    integer, dimension(ESMF_MAXDIM)        :: ec, dg2gm
    integer, dimension(ESMF_MAXDIM)        :: f2gm, gelb, geub
    logical, dimension(ESMF_MAXDIM)        :: flipflop
    integer                                :: forderIndex, i
    integer                                :: gridrank, arrayrank, uglb_size, ugub_size
    integer                                :: grid_repdimcount, gridrank_norep
    integer                                :: localDeCount, l_localDe


    ! Initialize
    localrc = ESMF_RC_NOT_IMPL
    if (present(rc)) rc = ESMF_RC_NOT_IMPL

    ESMF_INIT_CHECK_DEEP(ESMF_GeomGetInit,geom,rc)

    call ESMF_GeomGet(geom, localDeCount=localDeCount, &
      dimCount=gridrank, distgridToGridMap=dg2gm, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! default localDe    
    if(localDeCount .gt. 1 .and. (.not. present(localDe))) then
       call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
          msg="localDe must be present when localDeCount is greater than 1", &
           ESMF_CONTEXT, rcToReturn=rc)
       return
    endif 
    if(present(localDe)) then
        l_localDe = localDe
    else 
        l_localDe = 0
    endif

    call ESMF_GeomGetPLocalDE(geom, localDe=l_localDe, &
       exclusiveLBound=gelb, exclusiveUBound=geub, exclusiveCount=ec, rc=localrc)
    if (ESMF_LogFoundError(localrc, &
        ESMF_ERR_PASSTHRU, &
        ESMF_CONTEXT, rcToReturn=rc)) return

    ! Validate input arguments
    if(present(gridToFieldMap) ) then
        if(size(gridToFieldMap) .ne. gridrank) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="gridToFieldMap size must equal to grid dimension count", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif

    ! set up local gridToFieldMap
    grid_repdimcount = 0
    if(present(gridToFieldMap)) then
        l_g2fm(1:size(gridToFieldMap)) = gridToFieldMap
        do i = 1, size(gridToFieldMap)
            if(gridToFieldMap(i) == 0) grid_repdimcount = grid_repdimcount + 1
        enddo
    else
        do i = 1, ESMF_MAXDIM
            l_g2fm(i) = i
        enddo
    endif
    gridrank_norep = gridrank - grid_repdimcount
    ! gridToFieldMap elements must be in range 0...fieldRank and unique  
    ! algorithm to check element uniqueness:  
    !   run time: O(ESMF_MAXDIM)  
    !   memory:   O(2*ESMF_MAXDIM)  
    !          or O(ESMF_MAXDIM+ESMF_MAXDIM/sizeof(integer)) with bitvector  
    flipflop = .false.  
    do i = 1, gridrank
       if(l_g2fm(i) .lt. 0 .and. l_g2fm(i) .gt. arrayrank) then  
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &   
                 msg="- gridToFieldMap element must be within range 0...array rank", &  
                   ESMF_CONTEXT, rcToReturn=rc)   
           return  
       endif  
       if(l_g2fm(i) /= 0) then
           if(flipflop(l_g2fm(i))) then  
               call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &   
                     msg="- gridToFieldMap element must be unique", &  
                       ESMF_CONTEXT, rcToReturn=rc)   
               return  
           endif  
           flipflop(l_g2fm(i)) = .true.  
       endif
    enddo  

    ! User must either provide both ungriddedLBound and ungriddedUBound
    ! with same size or do not specify either one of them. There is no
    ! suitable default value for unbounded variables, especially when
    ! the intent is to create a Field with a greater rank than Grid
    if(present(ungriddedLBound)) then
        uglb_size = size(ungriddedLBound)
    else 
        uglb_size = 0
    endif
    if(present(ungriddedUBound)) then
        ugub_size = size(ungriddedUBound)
    else
        ugub_size = 0
    endif

    if(uglb_size .ne. ugub_size) then
       call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
          msg="ungriddedLBound and ungriddedUBound must have same size", &
           ESMF_CONTEXT, rcToReturn=rc)
       return
    endif 
    if(uglb_size .ne. 0) then
        allocate(l_uglb(uglb_size), l_ugub(ugub_size))
        l_uglb(1:uglb_size) = ungriddedLBound(1:uglb_size)
        l_ugub(1:ugub_size) = ungriddedUBound(1:ugub_size)
    endif

    ! the result Field/array rank
    arrayrank = gridrank + uglb_size
    arrayrank = arrayrank - grid_repdimcount

    ! check argument validity
    if(present(totalLBound)) then
        if(size(totalLBound) .ne. arrayrank) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="totalLBound size must equal to the desired array rank", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif
    if(present(totalUBound)) then
        if(size(totalUBound) .ne. arrayrank) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="totalUBound size must equal to the desired array rank", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif
    if(present(totalCount)) then
        if(size(totalCount) .ne. arrayrank) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="totalCount size must equal to the desired array rank", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif

    if(present(totalLWidth) ) then
        if(size(totalLWidth) .ne. gridrank_norep) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="totalLWidth size must equal to gridded dimension count", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif

    if(present(totalUWidth) ) then
        if(size(totalUWidth) .ne. gridrank_norep) then
           call ESMF_LogSetError(ESMF_RC_ARG_VALUE, &
              msg="totalUWidth size must equal to gridded dimension count", &
               ESMF_CONTEXT, rcToReturn=rc)
           return
        endif 
    endif

    ! At this point input arguments are validated
    ! allocate the return value arrays
    allocate(l_alb(arrayrank), l_aub(arrayrank), l_ac(arrayrank))
    l_mhlw = 0
    if(present(totalLWidth)) then
        l_mhlw(1:size(totalLWidth)) = totalLWidth
    endif
    l_mhuw = 0
    if(present(totalUWidth)) then
        l_mhuw(1:size(totalUWidth)) = totalUWidth
    endif

    ! First we compute the ungridded bounds:
    ! compute a reverse mapping from Field to Grid then
    ! compute ungridded Fortran array bounds
    f2gm = 0
    do i = 1, gridrank
        if(l_g2fm(i) /= 0) f2gm(l_g2fm(i)) = i
    enddo
    forderIndex = 1
    ! ungridded bounds info present indicates field has ungridded dimension
    ! otherwise we do not have to worry about this.
    if(uglb_size /= 0) then
        do i = 1, arrayrank
            ! if the i-th dimension is ungridded
            if(f2gm(i) .eq. 0) then
                l_alb(i) = l_uglb(forderIndex)
                l_aub(i) = l_ugub(forderIndex)
                l_ac(i)  = l_aub(i) - l_alb(i) + 1
                forderIndex = forderIndex + 1
            endif
        enddo
    endif
!XXX
    ! Next compute the gridded bounds using the mapping
    ! from Field to Grid computed in last step
    forderIndex = 1
    do i = 1, arrayrank
        ! if i-th dimension is gridded
        if(f2gm(i) .gt. 0) then
            l_ac(i) = ec(f2gm(i))+l_mhlw(forderIndex)+l_mhuw(forderIndex)
            l_alb(i) = gelb(f2gm(i)) - l_mhlw(forderIndex)
            l_aub(i) = l_alb(i) + l_ac(i) - 1
            forderIndex = forderIndex + 1
        endif
    enddo

    ! Prepare the return values
    if(present(totalLBound)) totalLBound(1:arrayrank) = l_alb(1:arrayrank)
    if(present(totalUBound)) totalUBound(1:arrayrank) = l_aub(1:arrayrank)
    if(present(totalCount))  totalCount(1:arrayrank)  = l_ac(1:arrayrank)

    ! deallocate temporary arrays
    if(uglb_size .ne. 0) then
        deallocate(l_uglb, l_ugub)
    endif
    deallocate(l_alb, l_aub, l_ac)

    if (present(rc)) rc = ESMF_SUCCESS
    end subroutine ESMF_FieldGetGBAllocBounds

end module ESMF_FieldGetAllocBoundsMod
