ESMF_FieldCreateEx.F90 Source File


Source Code

! $Id$
!
! Earth System Modeling Framework
! Copyright (c) 2002-2023, 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.
!
!==============================================================================
!
    program ESMF_FieldCreateEx

!------------------------------------------------------------------------------
!ESMF_MULTI_PROC_EXAMPLE        String used by test script to count examples.
!==============================================================================
! !PROGRAM: ESMF_FieldCreateEx - Field creation
!
! !DESCRIPTION:
!
! This program shows examples of Field initialization and manipulation
!-----------------------------------------------------------------------------
#include "ESMF.h"
    ! ESMF Framework module
    use ESMF_TestMod
    use ESMF
    implicit none
    
    ! Local variables

    real(ESMF_KIND_R8), dimension(:,:), allocatable     :: farray
    real(ESMF_KIND_R8), dimension(:,:), pointer         :: farrayPtr, farrayPtr2
    real(ESMF_KIND_R8), dimension(:,:,:), allocatable   :: farray3d
    integer, dimension(3)                                :: gec, fa_shape
    integer, dimension(2)           :: gridToFieldMap2d
    integer, dimension(2)           :: totalLWidth2d, totalUWidth2d
    type(ESMF_VM)                   :: vm
    type(ESMF_Field)                :: field, field1
    type(ESMF_Grid)                 :: grid
    type(ESMF_DistGrid)             :: distgrid
    type(ESMF_LocStream)            :: locs
    type(ESMF_Mesh)                 :: mesh
    type(ESMF_Arrayspec)            :: arrayspec
    type(ESMF_Array)                :: array
    integer                         :: i, k, localPet, petCount

    integer, pointer :: nodeIds(:),nodeOwners(:)
    real(ESMF_KIND_R8), pointer :: nodeCoords(:)
    integer :: numNodes
    integer :: numElems
    integer, pointer :: elemIds(:),elemTypes(:),elemConn(:)

    integer                         :: finalrc, rc, result

  character(ESMF_MAXSTR) :: testname
  character(ESMF_MAXSTR) :: failMsg

!-------------------------------------------------------------------------
!-------------------------------------------------------------------------

  write(failMsg, *) "Example failure"
  write(testname, *) "Example ESMF_FieldCreateEx"


! ------------------------------------------------------------------------------
! ------------------------------------------------------------------------------


!   !Set finalrc to success
    finalrc = ESMF_SUCCESS

    call ESMF_Initialize(vm=vm, defaultlogfilename="FieldCreateEx.Log", &
                    logkindflag=ESMF_LOGKIND_MULTI, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    if (.not. ESMF_TestMinPETs(4, ESMF_SRCLINE)) &
        call ESMF_Finalize(endflag=ESMF_END_ABORT)

    ! get global VM
    call ESMF_VMGetGlobal(vm, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_VMGet(vm, localPet=localPet, petCount=petCount, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a 2D Field with a 2D Grid and a Fortran data array}
!\label{sec:field:usage:create_2darray}
!
!  A user can create an {\tt ESMF\_Field} directly from an {\tt ESMF\_Grid} and an intrinsic 
!  Fortran data array. This interface is overloaded for typekind and rank
!  of the Fortran data array.  
!
!  In the following example, each dimension size of the Fortran array is equal to the 
!  exclusive bounds of its corresponding 
!  Grid dimension queried from the Grid through {\tt ESMF\_GridGet()} public interface.
!
!  Formally let fa\_shape(i) be the shape of i-th dimension of user supplied Fortran array,
!  then rule 1 states:  
!  \begin{verbatim}
! 
!  (1) fa_shape(i) = exclusiveCount(i)         
!                i = 1...GridDimCount
! 
!  \end{verbatim}
! 
!  fa\_shape(i) defines the shape of i-th dimension of the Fortran array.
!  ExclusiveCount are the number of data elements of i-th dimension in the exclusive region queried
!  from {\tt ESMF\_GridGet} interface. {\em Rule 1 assumes that the Grid and the Fortran intrinsic
!  array have same number of dimensions; and optional arguments
!  of FieldCreate from Fortran array are left unspecified using default setup}. These assumptions 
!  are true for most typical uses of FieldCreate from Fortran data array. This is the easiest way
!  to create a Field from a Grid and a Fortran intrinsic data array.
!  
!  Fortran array dimension sizes (called shape in most Fortran language books) are equivalent
!  to the bounds and counts used in this manual.  The following equation holds: 
!  \begin{verbatim}
! 
!  fa_shape(i) = shape(i) = counts(i) = upper_bound(i) - lower_bound(i) + 1
! 
!  \end{verbatim}
!
!  These typically mean the same concept unless specifically explained to mean something else.
!  For example, ESMF uses DimCount very often to mean number of dimensions instead of its meaning
!  implied in the above equation. We'll clarify the meaning of a word when ambiguity could occur.
!  
!  Rule 1 is most useful for a user working with Field creation from a Grid and a Fortran
!  data array in most scenarios. It extends to higher dimension count, 3D, 4D, etc...
!  Typically, as the code example demonstrates, a user first creates a Grid,
!  then uses {\tt ESMF\_GridGet()}
!  to retrieve the exclusive counts.  Next the user calculates the shape
!  of each Fortran array dimension according to rule 1. The Fortran data array is allocated
!  and initialized based on the computed shape.  A Field can either be created in one shot or
!  created empty and finished using {\tt ESMF\_FieldEmptyComplete}.
!
!  \begin{sloppypar}
!  There are important details that can be skipped but are good to know for {\tt ESMF\_FieldEmptyComplete}
!  and {\tt ESMF\_FieldCreate} from a Fortran data array. 1) these methods require {\em each PET contains
!  exactly one DE}. This implies that a code using FieldCreate from a data array or FieldEmptyComplete must
!  have the same number of DEs and PETs, formally $n_{DE} = n_{PET}$. Violation of this condition
!  will cause run time failures. 2) the bounds and counts retrieved from GridGet are DE specific
!  or equivalently PET specific, which means that {\em the Fortran array shape could be different from one
!  PET to another}. 
!  \end{sloppypar}
!  
!EOE

!BOC
    grid = ESMF_GridCreateNoPeriDim(minIndex=(/1,1/), maxIndex=(/10,20/), &
          regDecomp=(/2,2/), name="atmgrid", rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    call ESMF_GridGet(grid, localDE=0, staggerloc=ESMF_STAGGERLOC_CENTER, &
        exclusiveCount=gec, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    allocate(farray(gec(1), gec(2)) )

    field = ESMF_FieldCreate(grid, farray, ESMF_INDEX_DELOCAL, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC
    print *, "Field Create from a Grid and a Fortran data array returned"
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    deallocate(farray)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a 2D Field with a 2D Grid and a Fortran data pointer}
!\label{sec:field:usage:create_2dptr}
!
! The setup of this example is similar to the previous section except 
! that the Field is created from a data pointer instead of a data array.
! We highlight the ability to deallocate the internal Fortran data
! pointer queried from the Field. This gives a user more flexibility with
! memory management.
!
!EOE

!BOC
    allocate(farrayPtr(gec(1), gec(2)) )

    field = ESMF_FieldCreate(grid, farrayPtr, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_FieldGet(field, farrayPtr=farrayPtr2, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    ! deallocate the retrieved Fortran array pointer
    deallocate(farrayPtr2)
!EOC
    print *, "Field Create from a Grid and a Fortran data array returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a 3D Field with a 2D Grid and a 3D Fortran data array}
!\label{sec:field:usage:create_2dgrid_3dptr}
!
!  This example demonstrates a typical use of {\tt ESMF\_Field} combining
!  a 2D grid and a 3D Fortran native data array. One immediate problem follows: 
!  how does one define the bounds of the ungridded dimension? This is
!  solved by the optional arguments {\tt ungriddedLBound} and {\tt ungriddedUBound}
!  of the {\tt ESMF\_FieldCreate} interface. By definition, {\tt ungriddedLBound}
!  and {\tt ungriddedUBound}
!  are both 1 dimensional integer Fortran arrays.
!
!  Formally, let fa\_shape(j=1...FieldDimCount-GridDimCount) be the shape of the
!  ungridded dimensions of a Field relative to the Grid used in Field creation.
!  The Field dimension count is equal to the number of dimensions of the Fortran array, which
!  equals the number of dimensions of the resultant Field. GridDimCount is
!  the number of dimensions of the Grid. 
! 
!  fa\_shape(j) is computed as:
!  \begin{verbatim}
! 
!  fa_shape(j) = ungriddedUBound(j) - ungriddedLBound(j) + 1
! 
!  \end{verbatim}
!  
!  fa\_shape is easy to compute when the gridded and ungridded dimensions do not
!  mix. However, it's conceivable that at higher dimension count, gridded and ungridded
!  dimensions can interleave. To aid the computation of ungridded dimension shape
!  we formally introduce the mapping concept.
!
!  Let $map_{A,B}(i=1...n_A) = i_B$, and $i_B \in [\phi, 1...n_B]$. $n_A$ is the number
!  of elements in set A, $n_B$ is the number of elements in set B. $map_{A,B}(i)$ defines
!  a mapping from i-th element of set A to $i_B$-th element in set B. $i_B = \phi$ 
!  indicates there does not exist a mapping from i-th element of set A to set B.
!
!  Suppose we have a mapping from dimension index of ungriddedLBound (or
!  ungriddedUBound) to Fortran array dimension index, called ugb2fa. 
!  By definition, $n_A$ equals to the dimension count of
!  ungriddedLBound (or ungriddedUBound), $n_B$ equals to the dimension count of
!  the Fortran array. We can now formulate the computation of ungridded
!  dimension shape as rule 2:
!  \begin{verbatim}
! 
!  (2) fa_shape(ugb2fa(j)) = ungriddedUBound(j) - ungriddedLBound(j) + 1 
!                        j = 1..FortranArrayDimCount - GridDimCount 
!  \end{verbatim}
!
!  The mapping can be computed in linear time proportional to the
!  Fortran array dimension count (or rank) using the following algorithm in pseudocode:
!  \begin{verbatim}
!
!  map_index = 1
!  do i = 1, farray_rank
!      if i-th dimension of farray is ungridded
!          ugb2fa(map_index) = i
!          map_index = map_index + 1
!      endif
!  enddo
! 
!  \end{verbatim}
!
!  Here we use rank and dimension count interchangeably. These 2 terminologies are typically
!  equivalent. But there are subtle differences
!  under certain conditions. Rank is the total number of dimensions of a tensor object.
!  Dimension count allows a finer description of the heterogeneous dimensions in that object.
!  For example, a Field of rank 5 can have 3 gridded dimensions and 2 ungridded dimensions.
!  Rank is precisely the summation of dimension count of all types of dimensions. 
! 
!  For example, if a 5D array is used with a 3D Grid, there are 2 ungridded dimensions:
!  ungriddedLBound=(/1,2/) and ungriddedUBound=(/5,7/).
!  Suppose the distribution of dimensions looks like (O, X, O, X, O), O means gridded,
!  X means ungridded. Then the mapping from ungridded bounds to Fortran array is
!  ugb2fa=(/2, 4/). The shape of 2nd and 4th dimension of Fortran array should equal
!  (5, 8).
! 
!  Back to our 3D Field created from a 2D Grid and 3D Fortran array example, suppose the 3rd
!  Field dimension is ungridded, ungriddedLBound=(/3/), ungriddedUBound=(/9/).
!  First we use rule 1 to compute shapes of the gridded Fortran array dimension,
!  then we use rule 2 to compute shapes of the ungridded Fortran array dimension.
!  In this example, we used the exclusive bounds obtained in the previous
!  example.
!EOE

!BOC
    fa_shape(1) = gec(1) ! rule 1
    fa_shape(2) = gec(2)
    fa_shape(3) = 7 ! rule 2 9-3+1
    allocate(farray3d(fa_shape(1), fa_shape(2), fa_shape(3)))
    field = ESMF_FieldCreate(grid, farray3d, ESMF_INDEX_DELOCAL, &
        ungriddedLBound=(/3/), ungriddedUBound=(/9/), &
        rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC
    print *, "Field Create from a Grid and a Fortran data array returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    deallocate(farray3d)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a 3D Field with a 2D Grid and a 3D Fortran data array with gridToFieldMap argument}
!\label{sec:field:usage:create_2dgrid_3dptr_map}
!
!  Building upon the previous example, we will create a 3D Field from
!  a 2D grid and 3D array but with a slight twist. In this example, we
!  introduce the gridToFieldMap argument that allows a user to map Grid 
!  dimension index to Field dimension index.
!
!  In this example, both dimensions of the Grid are distributed and the
!  mapping from DistGrid to Grid is (/1,2/). We will introduce rule 3
!  assuming distgridToGridMap=(/1,2,3...gridDimCount/), and distgridDimCount equals
!  to gridDimCount. This is a reasonable assumption in typical Field use.
!
!  We apply the mapping gridToFieldMap on rule 1 to create rule 3:
!  \begin{verbatim}
! 
!  (3) fa_shape(gridToFieldMap(i)) = exclusiveCount(i)        
!                                i = 1,..GridDimCount.
! 
!  \end{verbatim}
!
!  Back to our example, suppose the 2nd
!  Field dimension is ungridded, ungriddedLBound=(/3/), ungriddedUBound=(/9/).
!  gridToFieldMap=(/3,1/), meaning the 1st Grid dimension maps to 3rd Field dimension,
!  and 2nd Grid dimension maps to 1st Field dimension.
!
!  First we use rule 3 to compute shapes of the gridded Fortran array dimension,
!  then we use rule 2 to compute shapes of the ungridded Fortran array dimension.
!  In this example, we use the exclusive bounds obtained in the previous
!  example.
!EOE
!BOC
    gridToFieldMap2d(1) = 3
    gridToFieldMap2d(2) = 1
    do i = 1, 2
        fa_shape(gridToFieldMap2d(i)) = gec(i)
    end do
    fa_shape(2) = 7
    allocate(farray3d(fa_shape(1), fa_shape(2), fa_shape(3)))
    field = ESMF_FieldCreate(grid, farray3d, ESMF_INDEX_DELOCAL, &
        ungriddedLBound=(/3/), ungriddedUBound=(/9/), &
        gridToFieldMap=gridToFieldMap2d, &
        rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC
    print *, "Field Create from a Grid and a Fortran data array returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    deallocate(farray3d)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a 3D Field with a 2D Grid and a 3D Fortran data array with halos}
!\label{sec:field:usage:create_2dgrid_3dptr_map_halo}
!
!  This example is similar to example \ref{sec:field:usage:create_2dgrid_3dptr_map}.
!  In addition, here we will show how
!  a user can associate different halo widths to a Fortran array to create
!  a Field through the totalLWidth and totalUWidth optional arguments.
!  A diagram of the dimension configuration from Grid, halos, and Fortran data array
!  is shown here.
!\begin{center}
!\begin{figure}
!\scalebox{0.75}{\includegraphics{FieldParameterSetup}}
!\caption{Field dimension configuration from Grid, halos, and Fortran data array.}
!\label{fig:fieldparameter}
!\end{figure}
!\end{center}
!  
!  The {\tt ESMF\_FieldCreate()} interface supports creating a Field from a Grid and a
!  Fortran array padded with halos on the distributed dimensions of the Fortran
!  array. Using this technique one can avoid passing non-contiguous Fortran array
!  slice to FieldCreate. It guarantees the same exclusive region,
!  and by using halos, it also defines a bigger total region to contain 
!  the entire contiguous memory block of the Fortran array.
!
!  The elements of totalLWidth and totalUWidth are applied in the order
!  distributed dimensions appear in the Fortran array. By definition, 
!  totalLWidth and totalUWidth are 1 dimensional arrays of non-negative 
!  integer values. The size of haloWidth arrays is equal to the number of distributed
!  dimensions of the Fortran array, which is also equal to the number of
!  distributed dimensions of the Grid used in the Field creation.
!
!  Because the order of totalWidth (representing both totalLWidth and
!  totalUWidth) element is applied to the order distributed dimensions
!  appear in the Fortran array dimensions, it's quite simple to compute
!  the shape of distributed dimensions of the Fortran array. They are done
!  in a similar manner when applying ungriddedLBound and ungriddedUBound 
!  to ungridded dimensions of the Fortran array defined by rule 2.
!
!  Assume we have the mapping from the dimension index of totalWidth
!  to the dimension index of Fortran array, called mhw2fa; and we also
!  have the mapping from dimension index of Fortran array to dimension
!  index of the Grid, called fa2g. The shape of
!  distributed dimensions of a Fortran array can be computed by rule 4: 
! 
!  \begin{verbatim}
!
!  (4) fa_shape(mhw2fa(k)) = exclusiveCount(fa2g(mhw2fa(k)) + 
!                            totalUWidth(k) + totalLWidth(k)
!                        k = 1...size(totalWidth) 
!
!  \end{verbatim}
!  
!  This rule may seem confusing but algorithmically the computation
!  can be done by the following pseudocode:
!
!  \begin{verbatim}
!
!  fa_index = 1
!  do i = 1, farray_rank
!     if i-th dimension of Fortran array is distributed
!         fa_shape(i) = exclusiveCount(fa2g(i)) + 
!                       totalUWidth(fa_index) + totalLWidth(fa_index)
!         fa_index = fa_index + 1
!     endif
!  enddo
!
!  \end{verbatim}
!
!  The only complication then is to figure out the mapping from Fortran
!  array dimension index to Grid dimension index. This process can
!  be done by computing the reverse mapping from Field to Grid.
!
!  Typically, we don't have to consider these complications if the following
!  conditions are met: 1) All Grid dimensions are distributed. 2) DistGrid
!  in the Grid has a dimension index mapping to the Grid in the form of 
!  natural order (/1,2,3,.../). This natural order mapping is the
!  default mapping between various objects throughout ESMF. 3) Grid to Field
!  mapping is in the form of natural order, i.e. default mapping. These
!  seem like a lot of conditions but they are the default case in the interaction
!  among DistGrid, Grid, and Field. When these conditions are met, which
!  is typically true, the shape of distributed dimensions of Fortran array
!  follows rule 5 in a simple form:
!
!  \begin{verbatim}
!
!  (5) fa_shape(k) = exclusiveCount(k) + 
!                    totalUWidth(k) + totalLWidth(k) 
!                k = 1...size(totalWidth)
!
!  \end{verbatim}
!
!  Let's examine an example on how to apply rule 5. Suppose we have a
!  5D array and a 3D Grid that has its first 3 dimensions mapped to the first
!  3 dimensions of the Fortran array. totalLWidth=(/1,2,3/), 
!  totalUWidth=(/7,9,10/), then by rule 5, the following pseudo code
!  can be used to compute the shape of the first 3 dimensions of the Fortran
!  array. The shape of the remaining two ungridded dimensions can be
!  computed according to rule 2.
!
!  \begin{verbatim}
!
!  do k = 1, 3
!      fa_shape(k) = exclusiveCount(k) + 
!                    totalUWidth(k) + totalLWidth(k)) 
!  enddo
!
!  \end{verbatim}
!
!  Suppose now gridToFieldMap=(/2,3,4/) instead which says
!  the first dimension of Grid maps to the 2nd dimension of Field (or 
!  Fortran array) and so on and so forth, we can obtain a more general form 
!  of rule 5 by introducing first\_distdim\_index shift when Grid to Field
!  map (gridToFieldMap) is in the form of (/a,a+1,a+2.../).
!
!  \begin{verbatim}
!
!  (6) fa_shape(k+first_distdim_index-1) = exclusiveCount(k) +
!                                          totalUWidth(k) + totalLWidth(k)
!                                      k = 1...size(totalWidth)
!
!  \end{verbatim}
!
!  It's obvious that first\_distdim\_index=a. If the first dimension of the Fortran
!  array is distributed, then rule 6 degenerates into rule 5, which is
!  the typical case.
!
!  Back to our example creating a 3D Field from a 2D Grid and a 3D intrinsic
!  Fortran array, we will use the Grid created from previous example
!  that satisfies condition 1 and 2. We'll also use a simple gridToFieldMap
!  (1,2) which is the default mapping that satisfies condition 3. 
!  First we use rule 5 to compute
!  the shape of distributed dimensions then we use rule 2 to compute the shape
!  of the ungridded dimensions.
!EOE  

!BOC
    gridToFieldMap2d(1) = 1
    gridToFieldMap2d(2) = 2
    totalLWidth2d(1) = 3
    totalLWidth2d(2) = 4
    totalUWidth2d(1) = 3
    totalUWidth2d(2) = 5
    do k = 1, 2
        fa_shape(k) = gec(k) + totalLWidth2d(k) + totalUWidth2d(k)
    end do
    fa_shape(3) = 7          ! 9-3+1
    allocate(farray3d(fa_shape(1), fa_shape(2), fa_shape(3)))
    field = ESMF_FieldCreate(grid, farray3d, ESMF_INDEX_DELOCAL, &
        ungriddedLBound=(/3/), ungriddedUBound=(/9/), &
        totalLWidth=totalLWidth2d, totalUWidth=totalUWidth2d, &
        gridToFieldMap=gridToFieldMap2d, &
        rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC
    print *, "Field Create from a Grid and a Fortran data array returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    deallocate(farray3d)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a LocStream, typekind, and rank}
!\label{sec:field:usage:create_locs_tkr}
! 
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_LocStream} 
! and typekind/rank.
! The location stream object is uniformly distributed
! in a 1 dimensional space on 4 DEs. The rank is 1 dimensional. 
! Please refer to LocStream examples section for more information on LocStream creation.
!
!EOE  
!BOC

    locs = ESMF_LocStreamCreate(minIndex=1, maxIndex=16, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    field = ESMF_FieldCreate(locs, typekind=ESMF_TYPEKIND_I4, &
        rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!EOC
    print *, "Field Create from a LocStream, typekind, and rank returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_LocStreamDestroy(locs,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a LocStream and arrayspec}
!\label{sec:field:usage:create_locs_arrayspec}
! 
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_LocStream} 
! and an {\tt ESMF\_Arrayspec}.
! The location stream object is uniformly distributed
! in a 1 dimensional space on 4 DEs. The arrayspec is 1 dimensional. 
! Please refer to LocStream examples section for more information on LocStream creation.
!
!EOE  
!BOC

    locs = ESMF_LocStreamCreate(minIndex=1, maxIndex=16, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    call ESMF_ArraySpecSet(arrayspec, 1, ESMF_TYPEKIND_I4, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    field = ESMF_FieldCreate(locs, arrayspec, &
        rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!EOC
    print *, "Field Create from a LocStream and an Arrayspec returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_LocStreamDestroy(locs,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a Mesh, typekind, and rank}
!\label{sec:field:usage:create_mesh_tkr}
! 
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_Mesh} 
! and typekind/rank.
! The mesh object is on a Euclidean surface that is partitioned to a 2x2 rectangular
! space with 4 elements and 9 nodes. The nodal space is represented by
! a distgrid with 9 indices. A Field is created on locally owned nodes on each PET.
! Therefore, the created Field has 9 data points globally.
! The mesh object can be represented by the picture
! below. For more information on Mesh creation, please see Section~\ref{sec:mesh:usage:meshCreation}.
! \begin{verbatim}
!              Mesh Ids
!
!  2.0   7 ------- 8 -------- 9
!        |         |          |
!        |    3    |    4     |
!        |         |          |
!  1.0   4 ------- 5 -------- 6
!        |         |          |
!        |    1    |    2     |
!        |         |          |
!  0.0   1 ------- 2 -------- 3
!
!       0.0       1.0        2.0 
!
!      Node Ids at corners
!      Element Ids in centers
! 
!
!             Mesh Owners
!
!  2.0   2 ------- 2 -------- 3
!        |         |          |
!        |    2    |    3     |
!        |         |          |
!  1.0   0 ------- 0 -------- 1
!        |         |          |
!        |    0    |    1     |
!        |         |          |
!  0.0   0 ------- 0 -------- 1
!
!       0.0       1.0        2.0 
!
!      Node Owners at corners
!      Element Owners in centers
!\end{verbatim} 
!
!EOE  
  ! Only do this if we have 4 PETs
   if (petCount .eq. 4) then
      ! Setup mesh data depending on PET
      if (localPet .eq. 0) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/1,2,4,5/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/0.0,0.0, &
                     1.0,0.0, &
                     0.0,1.0, &
                     1.0,1.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,0,0,0/) ! everything on proc 0

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/1/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)
      else if (localPet .eq. 1) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/2,3,5,6/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/1.0,0.0, &
                     2.0,0.0, &
                     1.0,1.0, &
                     2.0,1.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,1,0,1/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/2/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)
      else if (localPet .eq. 2) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/4,5,7,8/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/0.0,1.0, &
                     1.0,1.0, &
                     0.0,2.0, &
                     1.0,2.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,0,2,2/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/3/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)  
      else 
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/5,6,8,9/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/1.0,1.0, &
                     2.0,1.0, &
                     1.0,2.0, &
                     2.0,2.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,1,2,3/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/4/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)  
      endif

!BOC
      ! Create Mesh structure in 1 step
      mesh=ESMF_MeshCreate(parametricDim=2,spatialDim=2, &
             nodeIds=nodeIds, nodeCoords=nodeCoords, &
             nodeOwners=nodeOwners, elementIds=elemIds,&
             elementTypes=elemTypes, elementConn=elemConn, &
             rc=rc)
      if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

      ! Field is created on the 1 dimensional nodal distgrid. On
      ! each PET, Field is created on the locally owned nodes.
      field = ESMF_FieldCreate(mesh, typekind=ESMF_TYPEKIND_I4, rc=rc)
      if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC

      ! deallocate node data
      deallocate(nodeIds)
      deallocate(nodeCoords)
      deallocate(nodeOwners)

      ! deallocate elem data
      deallocate(elemIds)
      deallocate(elemTypes)
      deallocate(elemConn)

     ! Get rid of Mesh
     call ESMF_MeshDestroy(mesh, rc=rc)
     if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    ! endif for skip for != 4 procs
    endif 
    print *, "Field Create from a Mesh and typekind/rank returned"

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a Mesh and arrayspec}
!\label{sec:field:usage:create_mesh_arrayspec}
! 
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_Mesh} 
! and an {\tt ESMF\_Arrayspec}.
! The mesh object is on a Euclidean surface that is partitioned to a 2x2 rectangular
! space with 4 elements and 9 nodes. The nodal space is represented by
! a distgrid with 9 indices. Field is created on locally owned nodes on each PET.
! Therefore, the created Field has 9 data points globally.
! The mesh object can be represented by the picture
! below. For more information on Mesh creation, please see Section~\ref{sec:mesh:usage:meshCreation}.
!
!EOE  
  ! Only do this if we have 4 PETs
   if (petCount .eq. 4) then
      ! Setup mesh data depending on PET
      if (localPet .eq. 0) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/1,2,4,5/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/0.0,0.0, &
                     1.0,0.0, &
                     0.0,1.0, &
                     1.0,1.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,0,0,0/) ! everything on proc 0

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/1/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)
      else if (localPet .eq. 1) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/2,3,5,6/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/1.0,0.0, &
                     2.0,0.0, &
                     1.0,1.0, &
                     2.0,1.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,1,0,1/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/2/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)
      else if (localPet .eq. 2) then
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/4,5,7,8/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/0.0,1.0, &
                     1.0,1.0, &
                     0.0,2.0, &
                     1.0,2.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,0,2,2/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/3/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)  
      else 
         ! Fill in node data
         numNodes=4

        !! node ids
        allocate(nodeIds(numNodes))
        nodeIds=(/5,6,8,9/) 

        !! node Coords
        allocate(nodeCoords(numNodes*2))
        nodeCoords=(/1.0,1.0, &
                     2.0,1.0, &
                     1.0,2.0, &
                     2.0,2.0/)

        !! node owners
        allocate(nodeOwners(numNodes))
        nodeOwners=(/0,1,2,3/) 

        ! Fill in elem data
        numElems=1

        !! elem ids
        allocate(elemIds(numElems))
        elemIds=(/4/) 

        !! elem type
        allocate(elemTypes(numElems))
        elemTypes=ESMF_MESHELEMTYPE_QUAD

        !! elem conn
        allocate(elemConn(numElems*4))
        elemConn=(/1,2,4,3/)  
      endif

!BOC
      ! Create Mesh structure in 1 step
      mesh=ESMF_MeshCreate(parametricDim=2,spatialDim=2, &
             nodeIds=nodeIds, nodeCoords=nodeCoords, &
             nodeOwners=nodeOwners, elementIds=elemIds,&
             elementTypes=elemTypes, elementConn=elemConn, &
             rc=rc)
      if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

      call ESMF_ArraySpecSet(arrayspec, 1, ESMF_TYPEKIND_I4, rc=rc)
      if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

      ! Field is created on the 1 dimensional nodal distgrid. On
      ! each PET, Field is created on the locally owned nodes.
      field = ESMF_FieldCreate(mesh, arrayspec, rc=rc)
      if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC

      ! deallocate node data
      deallocate(nodeIds)
      deallocate(nodeCoords)
      deallocate(nodeOwners)

      ! deallocate elem data
      deallocate(elemIds)
      deallocate(elemTypes)
      deallocate(elemConn)
    ! endif for skip for != 4 procs
    endif 
    print *, "Field Create from a Mesh and an Arrayspec returned"

!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a Mesh and an Array}
!\label{sec:field:usage:create_mesh_array}
! 
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_Mesh} 
! and an {\tt ESMF\_Array}. The mesh object is created in the previous example and
! the array object is retrieved from the field created in the previous example too.
!
!EOE  

!BOC
    call ESMF_MeshGet(mesh, nodalDistgrid=distgrid, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    array = ESMF_ArrayCreate(distgrid=distgrid, arrayspec=arrayspec, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    ! query the array from the previous example
    call ESMF_FieldGet(field, array=array, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    ! create a Field from a mesh and an array
    field1 = ESMF_FieldCreate(mesh, array, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC

    print *, "Field Create from a Mesh and an Array returned"
    call ESMF_FieldDestroy(field1,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_ArrayDestroy(array, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)


!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!-------------------------------- Example -----------------------------
!>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>%
!BOE
!\subsubsection{Create a Field from a Mesh and an ArraySpec with optional features}
!\label{sec:field:usage:createMeshArrayspecOpt}
!
! In this example, an {\tt ESMF\_Field} is created from an {\tt ESMF\_Mesh}
! and an {\tt ESMF\_ArraySpec}. The mesh object is created in the previous example.
! The Field is also created with optional arguments such as ungridded dimensions
! and dimension mapping.
!
! In this example, the mesh is mapped to the 2nd dimension of the
! {\tt ESMF\_Field}, with its first dimension being the ungridded dimension with bounds 1,3.
!
!EOE
!BOC
    call ESMF_ArraySpecSet(arrayspec, 2, ESMF_TYPEKIND_I4, rc=rc)
    field = ESMF_FieldCreate(mesh, arrayspec=arrayspec, gridToFieldMap=(/2/), &
        ungriddedLBound=(/1/), ungriddedUBound=(/3/), rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!EOC
    print *, "Field Create from a Mesh and an ArraySpec with ungridded dimension returned"
    call ESMF_FieldDestroy(field,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
    call ESMF_MeshDestroy(mesh,rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)
!-------------------------------------------------------------------------
! Destroy objects
    call ESMF_GridDestroy(grid, rc=rc)
    if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT)

    ! IMPORTANT: ESMF_STest() prints the PASS string and the # of processors in the log
    ! file that the scripts grep for.
    call ESMF_STest((finalrc.eq.ESMF_SUCCESS), testname, failMsg, result, ESMF_SRCLINE)


!-------------------------------------------------------------------------
     call ESMF_Finalize(rc=rc)
!-------------------------------------------------------------------------

    if (rc.NE.ESMF_SUCCESS) finalrc = ESMF_FAILURE

    if (finalrc.EQ.ESMF_SUCCESS) then
        print *, "PASS: ESMF_FieldCreateEx.F90"
    else
        print *, "FAIL: ESMF_FieldCreateEx.F90"
    end if
end program ESMF_FieldCreateEx