module seq_map_mod

!---------------------------------------------------------------------
!
! Purpose:
!
! General mapping routines
! including self normalizing mapping routine with optional fraction
!       
! Author: T. Craig, Jan-28-2011
!
!---------------------------------------------------------------------

  use shr_kind_mod      ,only: R8 => SHR_KIND_R8, IN=>SHR_KIND_IN
  use shr_kind_mod      ,only: CL => SHR_KIND_CL, CX => SHR_KIND_CX
  use shr_sys_mod
  use shr_const_mod
  use shr_mct_mod, only: shr_mct_sMatPInitnc
  use mct_mod

  use seq_comm_mct
  use seq_mctext_mod, only : seq_mctext_gsmapextend

#ifdef USE_ESMF_LIB
  use esmf
  use esmfshr_mod
  use seq_map_esmf
#endif

  implicit none
  save
  private  ! except

!--------------------------------------------------------------------------
! Public interfaces
!--------------------------------------------------------------------------

  public :: seq_map_init_rcfile
  public :: seq_map_init_rearrolap
  public :: seq_map_init_rearrsplit
  public :: seq_map_initvect
  public :: seq_map_map
  public :: seq_map_mapvect
  public :: seq_map_readdata
#ifdef USE_ESMF_LIB
  public :: seq_map_register
#endif

  interface seq_map_avNorm ; module procedure &
    seq_map_avNormArr, &
    seq_map_avNormAvF
  end interface

  type seq_map
    logical :: copy_only
    logical :: rearrange_only
    logical :: esmf_map
    type(mct_rearr) :: rearr
    type(mct_sMatp) :: sMatp
    !---- for cart3d
    character(CL) :: cart3d_init
    real(R8), pointer :: slon_s(:),clon_s(:),slat_s(:),clat_s(:)
    real(R8), pointer :: slon_d(:),clon_d(:),slat_d(:),clat_d(:)
    !---- for npfix
    character(CL) :: npfix_init
    integer(IN)   :: cntfound      ! number found
    integer(IN)   :: cntf_tot      ! cntfound total
    integer(IN)   :: ni,nj         ! grid size
    integer(IN)   :: mpicom        ! mpicom
    real(R8)   ,pointer :: ilon1(:)      ! lon of input grid at highest latitude
    real(R8)   ,pointer :: ilat1(:)      ! lat of input grid at highest latitude
    real(R8)   ,pointer :: ilon2(:)      ! lon of input grid at highest latitude
    real(R8)   ,pointer :: ilat2(:)      ! lat of input grid at highest latitude
    real(R8)   ,pointer :: alphafound(:) ! list of found alphas
    real(R8)   ,pointer :: betafound(:)  ! list of found betas
    integer(IN),pointer :: mfound(:)     ! list of found ms
    integer(IN),pointer :: nfound(:)     ! list of found ns
    integer(IN),pointer :: gindex(:)     ! global index 
#ifdef USE_ESMF_LIB
    !---- import and export States for this mapper object, 
    !---- routehandle is stored in the exp_state for repeated remapping use
    type(ESMF_State)    :: imp_state
    type(ESMF_State)    :: exp_state
#endif
  end type seq_map
  public seq_map

!--------------------------------------------------------------------------
! Public data
!--------------------------------------------------------------------------

!--------------------------------------------------------------------------
! Private data
!--------------------------------------------------------------------------

  character(*),parameter :: seq_map_stroff = 'variable_unset'
  character(*),parameter :: seq_map_stron  = 'StrinG_is_ON'
  real(R8),parameter,private :: deg2rad = shr_const_pi/180.0_R8  ! deg to rads

#ifdef CPP_VECTOR
  logical :: usevector = .true.
#else
  logical :: usevector = .false.
#endif
  
#ifdef SYSUNICOS
  logical :: usealltoall = .true.
#else
  logical :: usealltoall = .false.
#endif

!=======================================================================
contains
!=======================================================================

!=======================================================================
#ifdef USE_ESMF_LIB
  subroutine seq_map_register(petlist, ccsm_comp, comp, import_state, export_state)
  
    implicit none
  
    integer, pointer                  :: petlist(:)
    type(ESMF_CplComp)                :: ccsm_comp
    type(ESMF_GridComp), intent(out)  :: comp
    type(ESMF_State),    intent(out)  :: import_state, export_state
  
    integer            :: rc
  
    comp = ESMF_GridCompCreate(name="seq map comp", petList=petlist, rc=rc)
    if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to create seq map comp')
    call ESMF_GridCompSetServices(comp, seq_map_esmf_register, rc=rc)
    if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to register atm comp')
    import_state = ESMF_StateCreate(name="seq map import", stateintent=ESMF_STATEINTENT_IMPORT, rc=rc)
    if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to create import seq map state')
    export_state = ESMF_StateCreate(name="seq map export", stateintent=ESMF_STATEINTENT_EXPORT, rc=rc)
    if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to create export seq map state')
  
  end subroutine
#endif

!=======================================================================

  subroutine seq_map_init_rearrsplit( mapper, gsmap_s, ID_s, gsmap_d, ID_d, ID_join, string)

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout) :: mapper
    type(mct_gsmap) ,intent(in)    :: gsmap_s
    integer(IN)     ,intent(in)    :: ID_s
    type(mct_gsmap) ,intent(in)    :: gsmap_d
    integer(IN)     ,intent(in)    :: ID_d
    integer(IN)     ,intent(in)    :: ID_join
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    integer(IN) :: mpicom_s, mpicom_d, mpicom_join
    type(mct_gsmap) :: gsmap_s_join
    type(mct_gsmap) :: gsmap_d_join
    character(len=*),parameter :: subname = "(seq_map_init_rearrsplit) "

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

    if (seq_comm_iamroot(CPLID) .and. present(string)) then
       write(logunit,'(A)') subname//' called for '//trim(string)
    endif

    mapper%copy_only = .false.
    mapper%rearrange_only = .false.
    mapper%esmf_map = .false.

    call seq_comm_setptrs(ID_join,mpicom=mpicom_join)
    mapper%mpicom = mpicom_join

    if (mct_gsmap_Identical(gsmap_s,gsmap_d)) then
       mapper%copy_only = .true.
       if (seq_comm_iamroot(ID_join)) then
          write(logunit,'(2A,L2)') subname,' gsmaps ARE IDENTICAL, copyoption = ',mapper%copy_only
       endif
    else
       if (seq_comm_iamroot(ID_join)) write(logunit,'(2A)') subname,' gsmaps are not identical'
       mapper%rearrange_only = .true.

       call seq_comm_setptrs(ID_s ,mpicom=mpicom_s)
       call seq_comm_setptrs(ID_d ,mpicom=mpicom_d)
       call seq_comm_setptrs(ID_join,mpicom=mpicom_join)

       ! --- Extend gsmaps to join group of pes

       call seq_mctext_gsmapExtend(gsmap_s, mpicom_s, gsmap_s_join, mpicom_join, ID_join)
       call seq_mctext_gsmapExtend(gsmap_d, mpicom_d, gsmap_d_join, mpicom_join, ID_join)

       ! --- Initialize rearranger based on join gsmaps

       call seq_map_gsmapcheck(gsmap_s_join, gsmap_d_join)
       call mct_rearr_init(gsmap_s_join, gsmap_d_join, mpicom_join, mapper%rearr)

       ! --- Clean up temporary gsmaps

       call mct_gsMap_clean(gsmap_s_join)
       call mct_gsMap_clean(gsmap_d_join)

    endif

  end subroutine seq_map_init_rearrsplit

!=======================================================================

  subroutine seq_map_init_rcfile( mapper, gsmap_s, gsmap_d, mpicom, &
       maprcfile, maprcname, maprctype, samegrid, string, esmf_map)

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout) :: mapper
    type(mct_gsmap) ,intent(in)    :: gsmap_s
    type(mct_gsmap) ,intent(in)    :: gsmap_d
    integer(IN)     ,intent(in)    :: mpicom
    character(len=*),intent(in)    :: maprcfile
    character(len=*),intent(in)    :: maprcname
    character(len=*),intent(in)    :: maprctype
    logical         ,intent(in)    :: samegrid
    character(len=*),intent(in),optional :: string
    logical         ,intent(in),optional :: esmf_map
    !
    ! Local Variables
    !
    integer(IN) :: ssize,dsize
    character(len=*),parameter :: subname = "(seq_map_init_rcfile) "

#ifdef USE_ESMF_LIB
    type(ESMF_GridComp)         :: comp
    type(ESMF_State)            :: imp_state, exp_state
    type(ESMF_Array)            :: gindex_s, gindex_d
    type(ESMF_Array)            :: factorArray, factorIndexArray
    integer, pointer            :: factorIndexList(:,:)
    real(ESMF_KIND_R8), pointer :: factorList(:)
    integer                     :: rc, urc, row_idx, col_idx, wgt_idx, nwgt
    logical                     :: has_weight=.true.
    integer, pointer            :: petmap(:)
#endif

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

    if (seq_comm_iamroot(CPLID) .and. present(string)) then
       write(logunit,'(A)') subname//' called for '//trim(string)
    endif

    mapper%copy_only = .false.
    mapper%rearrange_only = .false.
    mapper%esmf_map = .false.
    mapper%mpicom = mpicom

    if (present(esmf_map)) then
       mapper%esmf_map = esmf_map
    endif

    if (mct_gsmap_Identical(gsmap_s,gsmap_d)) then
       mapper%copy_only = .true.
    elseif (samegrid) then
       mapper%rearrange_only = .true.

       ! --- Initialize rearranger
       call seq_map_gsmapcheck(gsmap_s, gsmap_d)
       call mct_rearr_init(gsmap_s, gsmap_d, mpicom, mapper%rearr)

    else

       ! --- Initialize Smatp
       call shr_mct_sMatPInitnc(mapper%sMatp,gsMap_s,gsMap_d, &
            trim(maprcfile),trim(maprcname),trim(maprctype),mpicom)

       if (mapper%esmf_map) then
#ifdef USE_ESMF_LIB
          !------------------------------------------------------
          ! ESMF:
          ! Set up routehandle
          !   This section of the code handles the creation of routehandle
          ! for ESMF SMM communication calls. First, the existing MCT
          ! sparsematmul descriptor is reused (although this part can be
          ! done completely without MCT, doing a distributed IO from maprcfile).
          ! The weight matrix and indicies vectors are wrapped into ESMF Arrays
          ! based on simple block distributed DistGrid. 
          !   These Arrays are passed into a coupler component set up during
          ! CESM driver initialization and stored in global seq_comm_type array
          ! at the driver level. The coupler component is indexed by CPLID in
          ! the seq_comm_type array. In the coupler component initialization phase
          ! the SMM routehandle is computed and attached to export state of
          ! a specific mapper object. Routehandle in a mapper object can be reused
          ! for the same regridding model component pair.
          !
          ! Fei Liu
          !------------------------------------------------------
          ! has_weight can be controlled through namelist to determine
          ! if weights are computed offline or to be computed online
          ! during initialization.
          if(has_weight) then
            ! Retrieve weights and indicies from mapper object
            row_idx = mct_sMat_indexIA(mapper%sMatp%matrix, 'grow')
            col_idx = mct_sMat_indexIA(mapper%sMatp%matrix, 'gcol')
            wgt_idx = mct_sMat_indexRA(mapper%sMatp%matrix, 'weight')
            nwgt = size(mapper%sMatp%matrix%data%rattr(wgt_idx, :))
            !write(logunit,*) trim(string), row_idx, col_idx, wgt_idx, nwgt
            allocate(factorIndexList(2, nwgt), factorList(nwgt), stat=rc)
            if(rc /= 0) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
            factorIndexList(1,:) = mapper%sMatp%matrix%data%iattr(col_idx, :)
            factorIndexList(2,:) = mapper%sMatp%matrix%data%iattr(row_idx, :)
            factorList = mapper%sMatp%matrix%data%rattr(wgt_idx,:)

            ! Get coupler pet map in global setting
            call seq_comm_petlist(CPLID, petmap)

            ! Set up temporary arrays to compute ESMF SMM routes.
            gindex_s = mct2esmf_create(gsMap_s, mpicom=mpicom, petmap=petmap, &
                name="gindex_s", rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            gindex_d = mct2esmf_create(gsMap_d, mpicom=mpicom, petmap=petmap, &
                name="gindex_d", rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            factorArray = mct2esmf_create(factorList, mpicom=mpicom, petmap=petmap, &
                name="factorArray", rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            factorIndexArray = mct2esmf_create(factorIndexList, mpicom=mpicom, &
                petmap=petmap, name="factorIndexArray", rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            ! Get the coupler component
            ! Create mapper specific import and export States
            call seq_comm_getcompstates(CPLID, comp=comp)

            mapper%imp_state = ESMF_StateCreate(name="import", stateintent=ESMF_STATEINTENT_IMPORT, rc=rc)
            if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to create import atm state')
            mapper%exp_state = ESMF_StateCreate(name="export", stateintent=ESMF_STATEINTENT_EXPORT, rc=rc)
            if(rc /= ESMF_SUCCESS) call shr_sys_abort('failed to create export atm state')

            ! Attach Arrays to the States
            call ESMF_StateAdd(mapper%imp_state, (/gindex_s/), rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            call ESMF_StateAdd(mapper%exp_state, (/gindex_d/), rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            call ESMF_StateAdd(mapper%exp_state, (/factorArray/), rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            call ESMF_StateAdd(mapper%exp_state, (/factorIndexArray/), rc=rc)
            if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            ! Call into ESMF init method
            call ESMF_GridCompInitialize(comp, importState=mapper%imp_state, exportState=mapper%exp_state, &
                 userRc=urc, rc=rc)
            if (urc /= ESMF_SUCCESS) call ESMF_Finalize(rc=urc, endflag=ESMF_END_ABORT)
            if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

            deallocate(factorIndexList, factorList)

          else
            ! Compute the routehandle online here:
            ! 1. Read in source and destination Grid description files
            ! 2. Construct Grids or Meshes and dummy Fields on top
            ! 3. Call FieldRegridStore to compute routehandle
            ! 4. Save routehandle in mapper
          endif
#else
          call shr_sys_abort(subname//' ERROR: esmf SMM not allowed without USE_ESMF_LIB')
#endif
       endif  ! esmf_map

    endif

  end subroutine seq_map_init_rcfile

!=======================================================================

  subroutine seq_map_init_rearrolap( mapper, gsmap_s, gsmap_d, mpicom, string)

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout) :: mapper
    type(mct_gsmap) ,intent(in)    :: gsmap_s
    type(mct_gsmap) ,intent(in)    :: gsmap_d
    integer(IN)     ,intent(in)    :: mpicom
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    character(len=*),parameter :: subname = "(seq_map_init_rearrolap) "

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

    if (seq_comm_iamroot(CPLID) .and. present(string)) then
       write(logunit,'(A)') subname//' called for '//trim(string)
    endif

    mapper%copy_only = .false.
    mapper%rearrange_only = .false.
    mapper%mpicom = mpicom

    if (mct_gsmap_Identical(gsmap_s,gsmap_d)) then
       mapper%copy_only = .true.
    else
       mapper%rearrange_only = .true.

       ! --- Initialize rearranger
       call seq_map_gsmapcheck(gsmap_s, gsmap_d)
       call mct_rearr_init(gsmap_s, gsmap_d, mpicom, mapper%rearr)

    endif

  end subroutine seq_map_init_rearrolap

!=======================================================================

  subroutine seq_map_map( mapper, av_s, av_d, fldlist, norm, avwts_s, avwtsfld_s, string )

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout):: mapper
    type(mct_aVect) ,intent(in)   :: av_s
    type(mct_aVect) ,intent(inout):: av_d
    character(len=*),intent(in),optional :: fldlist
    logical         ,intent(in),optional :: norm
    type(mct_aVect) ,intent(in),optional :: avwts_s
    character(len=*),intent(in),optional :: avwtsfld_s
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    logical :: lnorm
    character(len=*),parameter :: subname = "(seq_map_map) "
    !-----------------------------------------------------

    if (seq_comm_iamroot(CPLID) .and. present(string)) then
       write(logunit,'(A)') subname//' called for '//trim(string)
    endif

    lnorm = .true.
    if (present(norm)) then
       lnorm = norm
    endif

    if (present(avwts_s) .and. .not. present(avwtsfld_s)) then
       write(logunit,*) subname,' ERROR: avwts_s present but avwtsfld_s not'
       call shr_sys_abort(subname//' ERROR: avwts present')
    endif
    if (.not. present(avwts_s) .and. present(avwtsfld_s)) then
       write(logunit,*) subname,' ERROR: avwtsfld_s present but avwts_s not'
       call shr_sys_abort(subname//' ERROR: avwtsfld present')
    endif

    if (mapper%copy_only) then
       !-------------------------------------------
       ! COPY data
       !-------------------------------------------
       if (present(fldlist)) then
          call mct_aVect_copy(aVin=av_s,aVout=av_d,rList=fldlist,vector=usevector)
       else
          call mct_aVect_copy(aVin=av_s,aVout=av_d,vector=usevector)
       endif

    else if (mapper%rearrange_only) then
       !-------------------------------------------
       ! REARRANGE data
       !-------------------------------------------
       if (present(fldlist)) then
          call mct_rearr_rearrange_fldlist(av_s, av_d, mapper%rearr, VECTOR=usevector, &
               ALLTOALL=usealltoall, fldlist=fldlist)
       else
          call mct_rearr_rearrange(av_s, av_d, mapper%rearr, VECTOR=usevector, &
               ALLTOALL=usealltoall)
       endif

    else
       !-------------------------------------------
       ! MAP data
       !-------------------------------------------
       if (present(avwts_s)) then
          if (present(fldlist)) then
             call seq_map_avNorm(mapper, av_s, av_d, avwts_s, trim(avwtsfld_s), &
                  rList=fldlist, norm=lnorm)
          else
             call seq_map_avNorm(mapper, av_s, av_d, avwts_s, trim(avwtsfld_s), &
                  norm=lnorm)
          endif
       else
          if (present(fldlist)) then
             call seq_map_avNorm(mapper, av_s, av_d, rList=fldlist, norm=lnorm)
          else
             call seq_map_avNorm(mapper, av_s, av_d, norm=lnorm)
          endif
       endif
    end if

  end subroutine seq_map_map

!=======================================================================
  subroutine seq_map_initvect( mapper, type, dom_s, dom_d, gsmap_s, ni, nj, string)

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout):: mapper
    character(len=*),intent(in)   :: type
    type(mct_gGrid) ,intent(in)   :: dom_s
    type(mct_gGrid) ,intent(inout):: dom_d
    type(mct_gsMap) ,intent(in),optional :: gsmap_s
    integer(IN)     ,intent(in),optional :: ni
    integer(IN)     ,intent(in),optional :: nj
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    integer(IN) :: klon, klat, lsize, n
    logical :: lnorm
    character(len=CL) :: lstring
    character(len=*),parameter :: subname = "(seq_map_initvect) "

    lstring = ' '
    if (present(string)) then
       if (seq_comm_iamroot(CPLID)) write(logunit,'(A)') subname//' called for '//trim(string)
       lstring = trim(string)
    endif

    if (trim(type) == 'npfix') then

       if (mapper%npfix_init == trim(seq_map_stron)) return

       if (.not.present(gsmap_s)) then
          call shr_sys_abort(trim(subname)//' ERROR gsmap_s required for npfix')
       endif
       if (.not.present(ni)) then
          call shr_sys_abort(trim(subname)//' ERROR ni required for npfix')
       endif
       if (.not.present(nj)) then
          call shr_sys_abort(trim(subname)//' ERROR nj required for npfix')
       endif
       mapper%ni = ni
       mapper%nj = nj
       call map_npFixNew4R(mapper,'init',gsmapi=gsmap_s,domi=dom_s,domo=dom_d)
       mapper%npfix_init = trim(seq_map_stron)

    elseif (trim(type(1:6)) == 'cart3d') then
       if (mapper%cart3d_init == trim(seq_map_stron)) return

       !--- compute these up front for vector mapping ---
       lsize = mct_aVect_lsize(dom_s%data)
       allocate(mapper%slon_s(lsize),mapper%clon_s(lsize), &
                mapper%slat_s(lsize),mapper%clat_s(lsize))
       klon = mct_aVect_indexRa(dom_s%data, "lon" )
       klat = mct_aVect_indexRa(dom_s%data, "lat" )
       do n = 1,lsize
          mapper%slon_s(n) = sin(dom_s%data%rAttr(klon,n)*deg2rad)
          mapper%clon_s(n) = cos(dom_s%data%rAttr(klon,n)*deg2rad)
          mapper%slat_s(n) = sin(dom_s%data%rAttr(klat,n)*deg2rad)
          mapper%clat_s(n) = cos(dom_s%data%rAttr(klat,n)*deg2rad)
       enddo
       
       lsize = mct_aVect_lsize(dom_d%data)
       allocate(mapper%slon_d(lsize),mapper%clon_d(lsize), &
                mapper%slat_d(lsize),mapper%clat_d(lsize))
       klon = mct_aVect_indexRa(dom_d%data, "lon" )
       klat = mct_aVect_indexRa(dom_d%data, "lat" )
       do n = 1,lsize
          mapper%slon_d(n) = sin(dom_d%data%rAttr(klon,n)*deg2rad)
          mapper%clon_d(n) = cos(dom_d%data%rAttr(klon,n)*deg2rad)
          mapper%slat_d(n) = sin(dom_d%data%rAttr(klat,n)*deg2rad)
          mapper%clat_d(n) = cos(dom_d%data%rAttr(klat,n)*deg2rad)
       enddo
       mapper%cart3d_init = trim(seq_map_stron)
    endif

  end subroutine seq_map_initvect

!=======================================================================
  subroutine seq_map_mapvect( mapper, type, av_s, av_d, fldu, fldv, norm, avwts_s, avwtsfld_s, string )

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout):: mapper
    character(len=*),intent(in)   :: type
    type(mct_aVect) ,intent(in)   :: av_s
    type(mct_aVect) ,intent(inout):: av_d
    character(len=*),intent(in)   :: fldu
    character(len=*),intent(in)   :: fldv
    logical         ,intent(in),optional :: norm
    type(mct_aVect) ,intent(in),optional :: avwts_s
    character(len=*),intent(in),optional :: avwtsfld_s
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    logical :: lnorm
    character(len=CL) :: lstring
    character(len=*),parameter :: subname = "(seq_map_mapvect) "
    !-----------------------------------------------------

    lstring = ' '
    if (present(string)) then
       if (seq_comm_iamroot(CPLID)) write(logunit,'(A)') subname//' called for '//trim(string)
       lstring = trim(string)
    endif

    if (mapper%copy_only .or. mapper%rearrange_only) then
       return
    endif

    if ((present(avwts_s) .and. .not. present(avwtsfld_s)) .or. &
        (present(avwtsfld_s) .and. .not. present(avwts_s))) then
       call shr_sys_abort(trim(subname)//' ERROR avwts_s avwtsfld_s consistency')
    endif

    lnorm = .true.
    if (present(norm)) then
       lnorm = norm
    endif

    if (trim(type) == 'npfix') then
       if (mapper%npfix_init /= trim(seq_map_stron)) then
          call shr_sys_abort(trim(subname)//' ERROR: npfix not initialized '//trim(lstring))
       endif
       call map_npFixNew4R(mapper,'run',buni=av_s,buno=av_d,fld1=fldu,fld2=fldv)
    elseif (trim(type(1:6)) == 'cart3d') then
       if (mapper%cart3d_init /= trim(seq_map_stron)) then
          call shr_sys_abort(trim(subname)//' ERROR: cart3d not initialized '//trim(lstring))
       endif
       if (present(avwts_s) .and. present(avwtsfld_s)) then
          call seq_map_cart3d(mapper, type, av_s, av_d, fldu, fldv, norm=lnorm, &
               avwts_s=avwts_s, avwtsfld_s=avwtsfld_s, string=string)
       else
          call seq_map_cart3d(mapper, type, av_s, av_d, fldu, fldv, norm=lnorm, string=string)
       endif
    elseif (trim(type) == 'none') then
       call seq_map_map(mapper, av_s, av_d, fldlist=trim(fldu)//':'//trim(fldv), norm=lnorm)
    else
       write(logunit,*) subname,' ERROR: type unsupported ',trim(type)
       call shr_sys_abort(trim(subname)//' ERROR in type='//trim(type))
    end if

  end subroutine seq_map_mapvect

!=======================================================================
  subroutine seq_map_cart3d( mapper, type, av_s, av_d, fldu, fldv, norm, avwts_s, avwtsfld_s, string)

    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    type(seq_map)   ,intent(inout):: mapper
    character(len=*),intent(in)   :: type
    type(mct_aVect) ,intent(in)   :: av_s
    type(mct_aVect) ,intent(inout):: av_d
    character(len=*),intent(in)   :: fldu
    character(len=*),intent(in)   :: fldv
    logical         ,intent(in),optional :: norm
    type(mct_aVect) ,intent(in),optional :: avwts_s
    character(len=*),intent(in),optional :: avwtsfld_s
    character(len=*),intent(in),optional :: string
    !
    ! Local Variables
    !
    integer           :: lsize
    logical           :: lnorm
    integer           :: ku,kv,kux,kuy,kuz,n
    real(r8)          :: ue,un,ur,ux,uy,uz,speed
    real(r8)          :: urmaxl,urmax,uravgl,uravg,spavgl,spavg
    type(mct_aVect)   :: av3_s, av3_d
    integer(in)       :: mpicom,my_task,ierr,urcnt,urcntl
    character(len=*),parameter :: subname = "(seq_map_cart3d) "

    lnorm = .true.
    if (present(norm)) then
       lnorm=norm
    endif

    mpicom = mapper%mpicom

    if ((present(avwts_s) .and. .not. present(avwtsfld_s)) .or. &
        (present(avwtsfld_s) .and. .not. present(avwts_s))) then
       call shr_sys_abort(trim(subname)//' ERROR avwts_s avwtsfld_s consistency')
    endif

    ku = mct_aVect_indexRA(av_s, trim(fldu), perrwith='quiet')
    kv = mct_aVect_indexRA(av_s, trim(fldv), perrwith='quiet')

    if (ku /= 0 .and. kv /= 0) then
       lsize = mct_aVect_lsize(av_s)
       call mct_avect_init(av3_s,rList='ux:uy:uz',lsize=lsize)

       lsize = mct_aVect_lsize(av_d)
       call mct_avect_init(av3_d,rList='ux:uy:uz',lsize=lsize)

       kux = mct_aVect_indexRA(av3_s,'ux')
       kuy = mct_aVect_indexRA(av3_s,'uy')
       kuz = mct_aVect_indexRA(av3_s,'uz')
       lsize = mct_aVect_lsize(av_s)
       do n = 1,lsize
          ur = 0.0_r8
          ue = av_s%rAttr(ku,n)
          un = av_s%rAttr(kv,n)
          ux = mapper%clon_s(n)*mapper%clat_s(n)*ur - &
               mapper%clon_s(n)*mapper%slat_s(n)*un - &
               mapper%slon_s(n)*ue
          uy = mapper%slon_s(n)*mapper%clon_s(n)*ur - &
               mapper%slon_s(n)*mapper%slat_s(n)*un + &
               mapper%clon_s(n)*ue
          uz = mapper%slat_s(n)*ur + &
               mapper%clat_s(n)*un
          av3_s%rAttr(kux,n) = ux
          av3_s%rAttr(kuy,n) = uy
          av3_s%rAttr(kuz,n) = uz
       enddo

       if (present(avwts_s) .and. present(avwtsfld_s)) then
          call seq_map_map(mapper, av3_s, av3_d, norm=lnorm, avwts_s=avwts_s, avwtsfld_s=avwtsfld_s)
       else
          call seq_map_map(mapper, av3_s, av3_d, norm=lnorm)
       endif

       kux = mct_aVect_indexRA(av3_d,'ux')
       kuy = mct_aVect_indexRA(av3_d,'uy')
       kuz = mct_aVect_indexRA(av3_d,'uz')
       lsize = mct_aVect_lsize(av_d)
       urmaxl = -1.0_r8
       uravgl = 0.0_r8
       urcntl = 0
       spavgl = 0.0_r8
       do n = 1,lsize
          ux = av3_d%rAttr(kux,n)
          uy = av3_d%rAttr(kuy,n)
          uz = av3_d%rAttr(kuz,n)
          ue = -mapper%slon_d(n)          *ux + &
                mapper%clon_d(n)          *uy
          un = -mapper%clon_d(n)*mapper%slat_d(n)*ux - &
                mapper%slon_d(n)*mapper%slat_d(n)*uy + &
                mapper%clat_d(n)*uz
          ur =  mapper%clon_d(n)*mapper%clat_d(n)*ux + &
                mapper%slon_d(n)*mapper%clat_d(n)*uy - &
                mapper%slat_d(n)*uz
          speed = sqrt(ur*ur + ue*ue + un*un)
          if (trim(type) == 'cart3d_diag' .or. trim(type) == 'cart3d_uvw_diag') then
             if (speed /= 0.0_r8) then
                urmaxl = max(urmaxl,abs(ur))
                uravgl = uravgl + abs(ur)
                spavgl = spavgl + speed
                urcntl = urcntl + 1
             endif
          endif
          if (type(1:10) == 'cart3d_uvw') then
             !--- this adds ur to ue and un, while preserving u/v angle and total speed ---
             if (un == 0.0_R8) then
                !--- if ue is also 0.0 then just give speed to ue, this is arbitrary ---
                av_d%rAttr(ku,n) = sign(speed,ue)
                av_d%rAttr(kv,n) = 0.0_r8
             else if (ue == 0.0_R8) then
                av_d%rAttr(ku,n) = 0.0_r8
                av_d%rAttr(kv,n) = sign(speed,un)
             else
                av_d%rAttr(ku,n) = sign(speed/sqrt(1.0_r8 + ((un*un)/(ue*ue))),ue)
                av_d%rAttr(kv,n) = sign(speed/sqrt(1.0_r8 + ((ue*ue)/(un*un))),un)
             endif
          else
             !--- this ignores ur ---
             av_d%rAttr(ku,n) = ue
             av_d%rAttr(kv,n) = un
          endif
       enddo
       if (trim(type) == 'cart3d_diag' .or. trim(type) == 'cart3d_uvw_diag') then
          call mpi_comm_rank(mpicom,my_task,ierr)
          call shr_mpi_max(urmaxl,urmax,mpicom,'urmax')
          call shr_mpi_sum(uravgl,uravg,mpicom,'uravg')
          call shr_mpi_sum(spavgl,spavg,mpicom,'spavg')
          call shr_mpi_sum(urcntl,urcnt,mpicom,'urcnt')
          if (my_task == 0 .and. urcnt > 0) then
             uravg = uravg / urcnt
             spavg = spavg / urcnt
             write(logunit,*) trim(subname),' cart3d uravg,urmax,spavg = ',uravg,urmax,spavg
          endif
       endif

      call mct_avect_clean(av3_s)
      call mct_avect_clean(av3_d)

   endif  ! ku,kv

  end subroutine seq_map_cart3d
!=======================================================================

  subroutine seq_map_readdata(maprcfile, maprcname, mpicom, ID, &
         ni_s, nj_s, av_s, gsmap_s, avfld_s, filefld_s, &
         ni_d, nj_d, av_d, gsmap_d, avfld_d, filefld_d, string)

    !--- lifted from work by J Edwards, April 2011

    use shr_pio_mod, only : shr_pio_getiosys, shr_pio_getiotype
    use pio, only : pio_openfile, pio_closefile, pio_read_darray, pio_inq_dimid, &
       pio_inq_dimlen, pio_inq_varid, file_desc_t, io_desc_t, iosystem_desc_t, &
       var_desc_t, pio_int, pio_get_var, pio_double, pio_initdecomp, pio_freedecomp
    implicit none
    !-----------------------------------------------------
    ! 
    ! Arguments
    !
    character(len=*),intent(in)    :: maprcfile
    character(len=*),intent(in)    :: maprcname
    integer(IN)     ,intent(in)    :: mpicom
    integer(IN)     ,intent(in)    :: ID
    integer(IN)     ,intent(out)  ,optional :: ni_s
    integer(IN)     ,intent(out)  ,optional :: nj_s
    type(mct_avect) ,intent(inout),optional :: av_s
    type(mct_gsmap) ,intent(in)   ,optional :: gsmap_s
    character(len=*),intent(in)   ,optional :: avfld_s
    character(len=*),intent(in)   ,optional :: filefld_s
    integer(IN)     ,intent(out)  ,optional :: ni_d
    integer(IN)     ,intent(out)  ,optional :: nj_d
    type(mct_avect) ,intent(inout),optional :: av_d
    type(mct_gsmap) ,intent(in)   ,optional :: gsmap_d
    character(len=*),intent(in)   ,optional :: avfld_d
    character(len=*),intent(in)   ,optional :: filefld_d
    character(len=*),intent(in)   ,optional :: string
    !
    ! Local Variables
    !
    type(iosystem_desc_t), pointer :: pio_subsystem
    integer(IN)       :: pio_iotype
    type(file_desc_t) :: File    ! PIO file pointer
    type(io_desc_t)   :: iodesc  ! PIO parallel io descriptor
    integer(IN)       :: rcode   ! pio routine return code
    type(var_desc_t)  :: vid     ! pio variable  ID
    integer(IN)       :: did     ! pio dimension ID
    integer(IN)       :: na      ! size of source domain
    integer(IN)       :: nb      ! size of destination domain
    integer(IN)       :: i       ! index
    integer(IN)       :: mytask  ! my task
    integer(IN), pointer :: dof(:)    ! DOF pointers for parallel read
    character(len=256):: fileName
    character(len=64) :: lfld_s, lfld_d, lfile_s, lfile_d
    character(*),parameter :: areaAV_field = 'aream'
    character(*),parameter :: areafile_s   = 'area_a'
    character(*),parameter :: areafile_d   = 'area_b'
    character(len=*),parameter :: subname  = "(seq_map_readdata) "
    !-----------------------------------------------------

    if (seq_comm_iamroot(CPLID) .and. present(string)) then
       write(logunit,'(A)') subname//' called for '//trim(string)
       call shr_sys_flush(logunit)
    endif

    call MPI_COMM_RANK(mpicom,mytask,rcode)

    lfld_s = trim(areaAV_field)
    if (present(avfld_s)) then
       lfld_s = trim(avfld_s)
    endif

    lfld_d = trim(areaAV_field)
    if (present(avfld_d)) then
       lfld_s = trim(avfld_d)
    endif

    lfile_s = trim(areafile_s)
    if (present(filefld_s)) then
       lfile_s = trim(filefld_s)
    endif

    lfile_d = trim(areafile_d)
    if (present(filefld_d)) then
       lfile_d = trim(filefld_d)
    endif

    call I90_allLoadF(trim(maprcfile),0,mpicom,rcode)
    if(rcode /= 0) then
       write(logunit,*)"Cant find maprcfile file ",trim(maprcfile)
       call shr_sys_abort(trim(subname)//"i90_allLoadF File Not Found")
    endif

    call i90_label(trim(maprcname),rcode)
    if(rcode /= 0) then
       write(logunit,*)"Cant find label ",maprcname
       call shr_sys_abort(trim(subname)//"i90_label Not Found")
    endif

    call i90_gtoken(filename,rcode)
    if(rcode /= 0) then
       write(logunit,*)"Error reading token ",filename
       call shr_sys_abort(trim(subname)//"i90_gtoken Error on filename read")
    endif

    pio_subsystem => shr_pio_getiosys(ID)
    pio_iotype = shr_pio_getiotype(ID)

    rcode = pio_openfile(pio_subsystem, File, pio_iotype, filename)

    if (present(ni_s)) then 
       rcode = pio_inq_dimid (File, 'ni_a', did)  ! number of lons in input grid
       rcode = pio_inq_dimlen(File, did  , ni_s)
    end if
    if(present(nj_s)) then
       rcode = pio_inq_dimid (File, 'nj_a', did)  ! number of lats in input grid
       rcode = pio_inq_dimlen(File, did  , nj_s)
    end if
    if(present(ni_d)) then
       rcode = pio_inq_dimid (File, 'ni_b', did)  ! number of lons in output grid
       rcode = pio_inq_dimlen(File, did  , ni_d)
    end if
    if(present(nj_d)) then
       rcode = pio_inq_dimid (File, 'nj_b', did)  ! number of lats in output grid
       rcode = pio_inq_dimlen(File, did  , nj_d)
    endif

    !--- read and load area_a ---
    if (present(av_s)) then
       if (.not.present(gsmap_s)) then
          call shr_sys_abort(trim(subname)//' ERROR av_s must have gsmap_s')
       endif
       rcode = pio_inq_dimid (File, 'n_a', did)  ! size of  input vector
       rcode = pio_inq_dimlen(File, did  , na)
       i = mct_avect_indexra(av_s, trim(lfld_s))
       call mct_gsmap_OrderedPoints(gsMap_s, mytask, dof)
       call pio_initdecomp(pio_subsystem, pio_double, (/na/), dof, iodesc)
       deallocate(dof)
       rcode = pio_inq_varid(File,trim(lfile_s),vid)
       call pio_read_darray(File, vid, iodesc, av_s%rattr(i,:), rcode)
       call pio_freedecomp(File,iodesc)
    end if

    !--- read and load area_b ---
    if (present(av_d)) then
       if (.not.present(gsmap_d)) then
          call shr_sys_abort(trim(subname)//' ERROR av_d must have gsmap_d')
       endif
       rcode = pio_inq_dimid (File, 'n_b', did)  ! size of output vector
       rcode = pio_inq_dimlen(File, did  , nb)
       i = mct_avect_indexra(av_d, trim(lfld_d))
       call mct_gsmap_OrderedPoints(gsMap_d, mytask, dof)
       call pio_initdecomp(pio_subsystem, pio_double, (/nb/), dof, iodesc)
       deallocate(dof)
       rcode = pio_inq_varid(File,trim(lfile_d),vid)
       call pio_read_darray(File, vid, iodesc, av_d%rattr(i,:), rcode)
       call pio_freedecomp(File,iodesc)
    endif


    call pio_closefile(File)

  end subroutine seq_map_readdata

!=======================================================================

  subroutine seq_map_avNormAvF(mapper, av_i, av_o, avf_i, avfifld, rList, norm)

    implicit none
    !-----------------------------------------------------
    !
    ! Arguments
    !
    type(seq_map)   , intent(inout) :: mapper! mapper
    type(mct_aVect) , intent(in)    :: av_i  ! input 
    type(mct_aVect) , intent(inout) :: av_o  ! output
    type(mct_aVect) , intent(in)    :: avf_i  ! extra src "weight"
    character(len=*), intent(in)    :: avfifld ! field name in avf_i
    character(len=*), intent(in),optional :: rList ! fields list
    logical         , intent(in),optional :: norm  ! normalize at end
    !
    integer(IN) :: lsize_i, lsize_f, lsize_o, kf, j
    real(r8),allocatable :: frac_i(:),frac_o(:)
    logical :: lnorm
    character(*),parameter :: subName = '(seq_map_avNormAvF) '
    !-----------------------------------------------------

    lnorm = .true.
    if (present(norm)) then
       lnorm = norm
    endif

    lsize_i = mct_aVect_lsize(av_i)
    lsize_f = mct_aVect_lsize(avf_i)

    if (lsize_i /= lsize_f) then
       write(logunit,*) subname,' ERROR: lsize_i ne lsize_f ',lsize_i,lsize_f
       call shr_sys_abort(subname//' ERROR size_i ne lsize_f')
    endif

    !--- extract frac_i field from avf_i to pass to seq_map_avNormArr ---
    allocate(frac_i(lsize_i))
    do j = 1,lsize_i
       kf = mct_aVect_indexRA(avf_i,trim(avfifld))
       frac_i(j) = avf_i%rAttr(kf,j)
    enddo

    if (present(rList)) then
       call seq_map_avNormArr(mapper, av_i, av_o, frac_i, rList=rList, norm=lnorm)
    else
       call seq_map_avNormArr(mapper, av_i, av_o, frac_i, norm=lnorm)
    endif

    deallocate(frac_i)

  end subroutine seq_map_avNormAvF

!=======================================================================

  subroutine seq_map_avNormArr(mapper, av_i, av_o, norm_i, rList, norm)

    implicit none
    !-----------------------------------------------------
    !
    ! Arguments
    !
    type(seq_map)   , intent(inout) :: mapper! mapper
    type(mct_aVect) , intent(in)    :: av_i  ! input 
    type(mct_aVect) , intent(inout) :: av_o  ! output
    real(r8)        , intent(in), optional :: norm_i(:)  ! source "weight"
    character(len=*), intent(in), optional :: rList ! fields list
    logical         , intent(in), optional :: norm  ! normalize at end
    !
    ! Local variables
    !
    type(mct_sMatp)        :: sMatp ! sMat
    type(mct_aVect)        :: avp_i , avp_o
    integer(IN)            :: i,j,ier,kf
    integer(IN)            :: lsize_i,lsize_o
    real(r8)               :: normval
    character(CX)          :: lrList
    logical                :: lnorm
    character(*),parameter :: subName = '(seq_map_avNormArr) '
    character(len=*),parameter :: ffld = 'norm8wt'  ! want something unique
#ifdef USE_ESMF_LIB
    type(ESMF_Array)    :: array_s, array_d, temparray_s, temparray_d
    type(ESMF_DistGrid) :: distgrid
    type(ESMF_GridComp) :: comp
    integer             :: rc, urc
#endif
    !-----------------------------------------------------

    sMatp   = mapper%sMatp
    lsize_i = mct_aVect_lsize(av_i)
    lsize_o = mct_aVect_lsize(av_o)

    lnorm = .true.
    if (present(norm)) then
       lnorm = norm
    endif

    if (present(norm_i) .and..not.lnorm) then
       write(logunit,*) subname,' ERROR: norm_i and norm = false'
       call shr_sys_abort(subname//' ERROR norm_i and norm = false')
    endif

    if (present(norm_i)) then
       if (size(norm_i) /= lsize_i) then
          write(logunit,*) subname,' ERROR: size(norm_i) ne lsize_i ',size(norm_i),lsize_i
          call shr_sys_abort(subname//' ERROR size(norm_i) ne lsize_i')
       endif
    endif

    !--- create temporary avs for mapping ---

    if (present(rList)) then
       call mct_aVect_init(avp_i, rList=trim( rList)//':'//ffld, lsize=lsize_i)
       call mct_aVect_init(avp_o, rList=trim( rList)//':'//ffld, lsize=lsize_o)
    else
       lrList = trim(mct_aVect_exportRList2c(av_i))
       call mct_aVect_init(avp_i, rList=trim(lrList)//':'//ffld, lsize=lsize_i)
       lrList = trim(mct_aVect_exportRList2c(av_o))
       call mct_aVect_init(avp_o, rList=trim(lrList)//':'//ffld, lsize=lsize_o)
    endif

    !--- copy av_i to avp_i and set ffld value to 1.0
    !--- then multiply all fields by norm_i if norm_i exists 
    !--- this will do the right thing for the norm_i normalization 

    call mct_aVect_copy(aVin=av_i, aVout=avp_i, VECTOR=usevector)
    kf = mct_aVect_indexRA(avp_i,ffld)
    do j = 1,lsize_i
       avp_i%rAttr(kf,j) = 1.0_r8
    enddo

    if (present(norm_i)) then
       do j = 1,lsize_i
          avp_i%rAttr(:,j) = avp_i%rAttr(:,j)*norm_i(j)
       enddo
    endif

    !--- map ---

    if (mapper%esmf_map) then
#ifdef USE_ESMF_LIB
       !--- ESMF SMM ---
       !   The 2nd part of the code in seq_map_mod.F90 handles the execution of ESMF SMM. Based
       ! on the number of input and output physical fields, temporary input and output ESMF Arrays
       ! are created and reference data pointer directly in input/output MCT AttributeVectors. These
       ! Arrays are attached to the import and export State retrieved from the mapper object of
       ! the regridding model component pair prior to the component run method. 
       ! Inside the component run method, a SMM execution is performed with the input/output Arrays
       ! and the routehandle. Afterwards, the temporary input/output Array are removed from 
       ! the States and destroyed.

       ! Create the temporary input array and attach to import State
       !
       ! Query DistGrid
       call ESMF_StateGet(mapper%imp_state, itemName='temparray_s', array=temparray_s, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
       call ESMF_ArrayGet(temparray_s, distgrid=distgrid, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       ! Create the temporary input array
       array_s = ESMF_ArrayCreate(distgrid=distgrid, farrayPtr=avp_i%rattr, &
           distgridToArrayMap=(/2/), &
           name='array_s', rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       call ESMF_AttributeSet(array_s, name='mct_names', &
           value=trim(mct_aVect_exportRList2c(avp_i)), rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       ! Attach to import State
       call ESMF_StateAdd(mapper%imp_state, (/array_s/), rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       ! Create the temporary output array and attach to export State
       call ESMF_StateGet(mapper%exp_state, itemName='temparray_d', array=temparray_d, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
       call ESMF_ArrayGet(temparray_d, distgrid=distgrid, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       array_d = ESMF_ArrayCreate(distgrid=distgrid, farrayPtr=avp_o%rattr, &
           distgridToArrayMap=(/2/), &
           name='array_d', rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       call ESMF_AttributeSet(array_d, name='mct_names', &
           value=trim(mct_aVect_exportRList2c(avp_o)), rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       call ESMF_StateAdd(mapper%exp_state, (/array_d/), rc=rc)
       if(rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       ! Query coupler gridcomp component and call into ESMF comp run method for SMM execution
       call seq_comm_getcompstates(CPLID, comp=comp)
       call ESMF_GridCompRun(comp, importState=mapper%imp_state, exportState=mapper%exp_state, &
           userRc=urc, rc=rc)
       if (urc /= ESMF_SUCCESS) call ESMF_Finalize(rc=urc, endflag=ESMF_END_ABORT)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       ! Remove the input/output arrays from import/export States and destroy them
       call ESMF_StateRemove(mapper%imp_state, itemName='array_s', rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
       call ESMF_ArrayDestroy(array_s, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)

       call ESMF_StateRemove(mapper%exp_state, itemName='array_d', rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
       call ESMF_ArrayDestroy(array_d, rc=rc)
       if (rc /= ESMF_SUCCESS) call ESMF_Finalize(rc=rc, endflag=ESMF_END_ABORT)
#else
       call shr_sys_abort(subname//' ERROR: esmf SMM not allowed without USE_ESMF_LIB')
#endif

    else
       ! MCT based SMM
       call mct_sMat_avMult(avp_i, sMatp, avp_o, VECTOR=usevector)
    endif


    !--- renormalize avp_o by mapped norm_i  ---

    if (lnorm) then
       do j = 1,lsize_o
          kf = mct_aVect_indexRA(avp_o,ffld)
          normval = avp_o%rAttr(kf,j)
          if (normval /= 0.0_r8) then
             normval = 1.0_r8/normval
          endif
          avp_o%rAttr(:,j) = avp_o%rAttr(:,j)*normval
       enddo
    endif

    !--- copy back into av_o and we are done ---

    call mct_aVect_copy(aVin=avp_o, aVout=av_o, VECTOR=usevector)

    call mct_aVect_clean(avp_i)
    call mct_aVect_clean(avp_o)

 end subroutine seq_map_avNormArr

!=======================================================================

 subroutine map_npFixNew4R(mapper,mode,buni,buno,fld1,fld2,gsmapi,domi,domo)

   !===============================================================================
   !    Correct the north pole mapping of velocity fields from the atm to ocn
   !    grids.  This assumes the input grid is a regular lat/lon with the north
   !    pole surrounded by the last latitude line in the input array.  The
   !    longitudes in the last latitude must be ordered and equally spaced.
   !
   !    4R is a low memory version of 3R.
   !    This version (New4R) is the same as 3R except it uses a lot less memory
   !    and is a bit faster.  Like 3R, it saves data between calls and so
   !    assumes the input grid remains constant for all calls.  This is bfb
   !    with 3R on bluevista as of 2/15/07.
   !
   !    !REVISION HISTORY:
   !    2007-Feb-12 - T. Craig -- modified New3R to reduce memory
   !    2007-Apr-27 - M. Vertenstein - implemented in sequential system
   !===============================================================================
   
#include <mpif.h>  
   ! 
   ! Arguments
   !
   type(seq_map)  ,intent(inout):: mapper  ! mapper
   character(*)   ,intent(in)   :: mode    ! init or run
   type(mct_Avect),intent(in)   ,optional:: buni    ! input  attribute vec
   type(mct_Avect),intent(inout),optional:: buno    ! output attribute vec
   character(*)   ,intent(in)   ,optional:: fld1    ! name of first input field
   character(*)   ,intent(in)   ,optional:: fld2    ! name of second input field
   type(mct_gsMap),intent(in)   ,optional:: gsmapi  ! input gsmap
   type(mct_gGrid),intent(in)   ,optional:: domi    ! input domain 
   type(mct_gGrid),intent(in)   ,optional:: domo    ! output domain 
   !
   ! Local Variables
   !
   integer(IN)  :: n,m                     ! generic indices
   integer(IN)  :: n1,n2,n3                ! generic indices
   integer(IN)  :: kui,kvi                 ! field indices
   integer(IN)  :: kuo,kvo                 ! field indices
   integer(IN)  :: kin                     ! index index
   integer(IN)  :: nmin,nmax               ! indices of highest latitude in input
   integer(IN)  :: npts                    ! local number of points in an aV
   integer(IN)  :: num                     ! number of points at highest latitude
   integer(IN)  :: kloni                   ! longitude index on input domain
   integer(IN)  :: klati                   ! latitude index on input domain
   integer(IN)  :: klono                   ! longitude index on output domain
   integer(IN)  :: klato                   ! latitude index on output domain
   integer(IN)  :: index                   ! index value
   real(R8)     :: rindex                  ! index value
   real(R8)     :: latmax                  ! value of highest latitude
   real(R8)     :: olon,olat               ! output bundle lon/lat
   real(R8)     :: ilon,ilat               ! input bundle lon/lat
   real(R8)     :: npu,npv                 ! np velocity fields relative to lon
   real(R8)     :: theta1,theta2           ! angles for trig functions
   real(R8),allocatable :: rarray(:)       ! temporary array
   real(R8),allocatable :: rarray2(:,:)    ! temporary array
   real(R8)     :: w1,w2,w3,w4             ! weights
   real(R8)     :: f1,f2,f3,f4             ! function values
   real(R8)     :: alpha,beta              ! used to generate weights
   real(R8)     :: rtmp                    ! real temporary
   real(R8),allocatable :: lData(:,:)      ! last lat local input bundle data
   integer      :: mpicom                  ! mpi communicator group 
   real(R8)   ,allocatable :: rfound(:)    ! temporary for copy
   integer(IN),allocatable :: ifound(:)    ! temporary for copy
   integer(IN)  :: cnt                     ! loop counter
   logical      :: found                   ! search for new interpolation
   integer(IN)  :: rcode                   ! error code
   integer(IN)  :: np1                     ! n+1 or tmp
   integer(IN)  :: mype

   !--- formats ---
   character(*),parameter :: subName = '(map_npFixNew4R) '
   character(*),parameter :: F00 = "('(map_npFixNew4R) ',8a)"
   character(*),parameter :: F01 = "('(map_npFixNew4R) ',a,i12)"
   !-------------------------------------------------------------------------------

   mpicom = mapper%mpicom
   call MPI_COMM_RANK(mpicom,mype,rcode)

   nmin = (mapper%ni)*(mapper%nj-1) + 1
   nmax = mapper%ni*mapper%nj
   num  = mapper%ni

   !---------------------------------------------------------------------------
   ! Initialization only
   !---------------------------------------------------------------------------

   if (trim(mode) == 'init') then

     if (loglevel > 0) write(logunit,F00) " compute bilinear weights & indicies for NP region."

!    tcx 3/19/08, don't use GlobGridNum, it's not set properly in models
!    kin   = mct_aVect_indexIA(domi%data,"GlobGridNum",perrWith=subName)
     klati = mct_aVect_indexRA(domi%data,"lat"        ,perrWith=subName)
     kloni = mct_aVect_indexRA(domi%data,"lon"        ,perrWith=subName)
     klato = mct_aVect_indexRA(domo%data,"lat"        ,perrWith=subName)
     klono = mct_aVect_indexRA(domo%data,"lon"        ,perrWith=subName)

     allocate(mapper%ilon1(num))
     allocate(mapper%ilon2(num))
     allocate(mapper%ilat1(num))
     allocate(mapper%ilat2(num))

     mapper%ilon1 = 0._r8
     mapper%ilon2 = 0._r8
     mapper%ilat1 = 0._r8
     mapper%ilat2 = 0._r8

     npts = mct_aVect_lSize(domi%data)
     call mct_gsMap_orderedPoints(gsMapi, mype, mapper%gindex)
     do m=1,npts
       if (mapper%gindex(m).ge.nmin) then               ! are on highest latitude
         n = mapper%gindex(m) - nmin + 1                ! n is 1->mapper%ni lon index on highest latitude
         rtmp = domi%data%rAttr(kloni,m)      ! rtmp is longitude value on highest latitude
         mapper%ilon1(n) = mod(rtmp+360._R8,360._R8) ! ilon1(n) is longitude val mapped from 0->360
         mapper%ilat1(n) = domi%data%rAttr(klati,m)  ! ilat1(n) values should all be the same (i.e. highest lat)
       endif
     enddo

     !--- all gather local data, MPI_SUM is low memory and simple
     !--- but is a performance penalty compared to gatherv and copy
     !--- or a fancy send/recv 

     allocate(rarray(num))
     rarray = mapper%ilat1
     call MPI_ALLREDUCE(rarray,mapper%ilat1,num,MPI_REAL8,MPI_SUM,mpicom,rcode)
     if (rcode.ne.0) then
       write(logunit,*) trim(subName),' ilat1 rcode error ',rcode
       call shr_sys_abort()
     endif
     rarray = mapper%ilon1
     call MPI_ALLREDUCE(rarray,mapper%ilon1,num,MPI_REAL8,MPI_SUM,mpicom,rcode)
     if (rcode.ne.0) then
       write(logunit,*) trim(subName),' ilon1 rcode error ',rcode
       call shr_sys_abort()
     endif

     do n = 1,num
       np1 = mod(n,num)+1
       mapper%ilat2(n) = mapper%ilat1(np1)
       mapper%ilon2(n) = mapper%ilon1(np1)
       if (mapper%ilon2(n) < mapper%ilon1(n)) mapper%ilon2(n) = mapper%ilon2(n) + 360._R8
     enddo

     do n = 1,num
       if (mapper%ilat1(n) /= mapper%ilat2(n)) then
          write(logunit,*) trim(subname),' ERROR: ilat1 ne ilat2 ',n,mapper%ilat1(n),mapper%ilat2(n)
          call shr_sys_abort(trim(subname)//' ERROR: ilat1 ne ilat2')
       endif
       if (mapper%ilon2(n) < mapper%ilon1(n)) then
          write(logunit,*) trim(subname),' ERROR: ilon2 lt ilon1 ',n,mapper%ilon1(n),mapper%ilon2(n)
          call shr_sys_abort(trim(subname)//' ERROR: ilon2 ilon1 error')
       endif
       ! tcraig check that lon diffs are reasonable 4x average dlon seems like reasonable limit
       if (mapper%ilon2(n) - mapper%ilon1(n) > (360.0_R8/(num*1.0_R8))*4.0) then
          write(logunit,*) trim(subname),' ERROR: ilon1,ilon2 ',n,mapper%ilon1(n),mapper%ilon2(n)
          call shr_sys_abort(trim(subname)//' ERROR: ilon2 ilon1 size diff')
       endif
     enddo

     latmax = maxval(mapper%ilat1)

     !--- compute weights and save them ---

     npts = mct_aVect_lSize(domo%data)
     allocate(mapper%mfound(npts),mapper%nfound(npts),mapper%alphafound(npts),mapper%betafound(npts))
     mapper%cntfound = 0

     do m = 1,npts
       olat = domo%data%rAttr(klato,m)
       if (olat >= latmax) then
         rtmp = domo%data%rAttr(klono,m)
         olon = mod(rtmp,360._R8)
         n = 1
         found = .false.
         do while (n <= num .and. .not.found )
           if (    olon         >= mapper%ilon1(n) .and. olon < mapper%ilon2(n) .or.   &
                   olon+360._R8 >= mapper%ilon1(n) .and. olon < mapper%ilon2(n)) then


!tcx ilat2==ilat1 so don't average
!--->        ilat = (mapper%ilat1(n) + mapper%ilat2(n)) * 0.5_R8
             ilat = mapper%ilat1(n)
             if (mapper%ilon2(n) == mapper%ilon1(n)) then
               alpha = 0.5_R8
             else if (    olon >= mapper%ilon1(n) .and. olon < mapper%ilon2(n)) then
               alpha = (olon - mapper%ilon1(n)) / (mapper%ilon2(n) - mapper%ilon1(n))
             else if (olon+360._R8>= mapper%ilon1(n) .and. olon < mapper%ilon2(n)) then
               alpha = (olon+360._R8 - mapper%ilon1(n)) / (mapper%ilon2(n) - mapper%ilon1(n))
             else
               write(logunit,*) subName,' ERROR: olon ',olon,mapper%ilon1(n),mapper%ilon2(n)
             endif
             if (ilat >= 90._R8) then
               beta  = 1.0_R8
             else
               beta  = (olat - ilat) / (90._R8 - ilat)
             endif

             mapper%cntfound = mapper%cntfound + 1
             mapper%mfound(mapper%cntfound) = m
             mapper%nfound(mapper%cntfound) = n
             mapper%alphafound(mapper%cntfound) = alpha
             mapper%betafound(mapper%cntfound) = beta
             found = .true.

           endif
           n = n + 1     ! normal increment
         enddo
         if ( .not.found ) then
           write(logunit,*) subName,' ERROR: found = false ',found,m,olon,olat
         endif
       endif
     end do

     allocate(ifound(npts))
     ifound(1:mapper%cntfound) = mapper%mfound(1:mapper%cntfound)
     deallocate(mapper%mfound)
     if (mapper%cntfound > 0) then
        allocate(mapper%mfound(mapper%cntfound))
        mapper%mfound(1:mapper%cntfound) = ifound(1:mapper%cntfound)
     endif

     ifound(1:mapper%cntfound) = mapper%nfound(1:mapper%cntfound)
     deallocate(mapper%nfound)
     if (mapper%cntfound > 0) then
        allocate(mapper%nfound(mapper%cntfound))
        mapper%nfound(1:mapper%cntfound) = ifound(1:mapper%cntfound)
     endif
     deallocate(ifound)

     allocate(rfound(npts))
     rfound(1:mapper%cntfound) = mapper%alphafound(1:mapper%cntfound)
     deallocate(mapper%alphafound)
     if (mapper%cntfound > 0) then
        allocate(mapper%alphafound(mapper%cntfound))
        mapper%alphafound(1:mapper%cntfound) = rfound(1:mapper%cntfound)
     endif

     rfound(1:mapper%cntfound) = mapper%betafound(1:mapper%cntfound)
     deallocate(mapper%betafound)
     if (mapper%cntfound > 0) then
        allocate(mapper%betafound(mapper%cntfound))
        mapper%betafound(1:mapper%cntfound) = rfound(1:mapper%cntfound)
     endif
     deallocate(rfound)

     call MPI_ALLREDUCE(mapper%cntfound,mapper%cntf_tot,1,MPI_INTEGER,MPI_SUM,mpicom,rcode)
     if (mype == 0) then
        write(logunit,F01) ' total npfix points found = ',mapper%cntf_tot
     endif

   elseif (trim(mode) == 'run') then

      !---------------------------------------------------------------------------
      ! Return if there is nothing to do; must be no points on any pes
      ! If there are any npfix points, all pes must continue to the np u,v calc
      !---------------------------------------------------------------------------

      if (mapper%cntf_tot < 1) then
         return
      endif

      !---------------------------------------------------------------------------
      ! Non-initialization, run-time fix
      !---------------------------------------------------------------------------

      !--- barrier not required but interesting for timing. ---
      !  call shr_mpi_barrier(mpicom,subName//" barrier")

      !--- extract index, u, v from buni ---
      kui   = mct_aVect_indexRA(buni,fld1,perrWith=subName)
      kvi   = mct_aVect_indexRA(buni,fld2,perrWith=subName)
      kuo   = mct_aVect_indexRA(buno,fld1,perrWith=subName)
      kvo   = mct_aVect_indexRA(buno,fld2,perrWith=subName)

      allocate(lData(3,num))
      lData = 0._R8
      npts = mct_aVect_lSize(buni)
      do n=1,npts
        if (mapper%gindex(n).ge.nmin) then
          m = mapper%gindex(n) - nmin + 1
          lData(1,m) = mapper%gindex(n)
          lData(2,m) = buni%rAttr(kui,n)
          lData(3,m) = buni%rAttr(kvi,n)
        endif
      enddo
   
      !--- all gather local data, MPI_SUM is low memory and simple
      !--- but is a performance penalty compared to gatherv and copy
      !--- KLUDGE - this should be looked at when it becomes a performance/memory
      !--- penalty   
   
      allocate(rarray2(3,num))
      rarray2=lData
      call MPI_ALLREDUCE(rarray2,lData,3*num,MPI_REAL8,MPI_SUM,mpicom,rcode)
      deallocate(rarray2)
   
      if (rcode.ne.0) then
        write(logunit,*) trim(subName),' rcode error ',rcode
        call shr_sys_abort()
      endif
   
      do n2=1,num
        if (lData(1,n2).lt.0.1_R8) then
          write(logunit,*) trim(subName),' error allreduce ',n2
        endif
      enddo
   
      !--- compute npu, npv (pole data) and initialize ilon,ilat arrays ---
   
      npu = 0._R8
      npv = 0._R8
      do n = 1,num
        theta1 = mapper%ilon1(n)*deg2rad
        npu = npu + cos(theta1)*lData(2,n) &
                  - sin(theta1)*lData(3,n)
        npv = npv + sin(theta1)*lData(2,n) &
                  + cos(theta1)*lData(3,n)
      enddo
      npu = npu / real(num,R8)
      npv = npv / real(num,R8)
   
      !--- compute updated pole vectors ---
   
   !DIR$ CONCURRENT
      do cnt = 1,mapper%cntfound
         m     = mapper%mfound(cnt)
         n     = mapper%nfound(cnt)
         np1   = mod(n,num)+1
         alpha = mapper%alphafound(cnt)
         beta  = mapper%betafound(cnt)
   
         w1 = (1.0_R8-alpha)*(1.0_R8-beta)
         w2 = (    alpha)*(1.0_R8-beta)
         w3 = (    alpha)*(    beta)
         w4 = (1.0_R8-alpha)*(    beta)
   
         theta1 = mapper%ilon1(n)*deg2rad
         theta2 = mapper%ilon2(n)*deg2rad
   
         f1 = lData(2,n)
         f2 = lData(2,np1)
         f3 =  cos(theta1)*npu + sin(theta1)*npv
         f4 =  cos(theta2)*npu + sin(theta2)*npv
         rtmp = w1*f1 + w2*f2 + w3*f3 + w4*f4
         buno%rAttr(kuo,m) = w1*f1 + w2*f2 + w3*f3 + w4*f4
   
         f1 = lData(3,n)
         f2 = lData(3,np1)
         f3 = -sin(theta1)*npu + cos(theta1)*npv
         f4 = -sin(theta2)*npu + cos(theta2)*npv
         rtmp = w1*f1 + w2*f2 + w3*f3 + w4*f4
         buno%rAttr(kvo,m) = w1*f1 + w2*f2 + w3*f3 + w4*f4
      enddo
   
      deallocate(lData)

   else

      call shr_sys_abort(trim(subname)//' ERROR: mode not valid '//trim(mode))

   endif

end subroutine map_npFixNew4R

!===============================================================================
 subroutine seq_map_gsmapcheck(gsmap1,gsmap2)

    implicit none
    type(mct_gsMap),intent(in) :: gsmap1
    type(mct_gsMap),intent(in) :: gsmap2

    integer(IN) :: s1, s2
    character(*),parameter :: subName = '(seq_map_gsmapcheck) '

    s1 = mct_gsMap_gsize(gsMap1)
    s2 = mct_gsMap_gsize(gsMap2)
    if (s1 /= s2) then
      write(logunit,*) trim(subname),'gsmap global sizes different ',s1,s2
      call shr_sys_abort(subName // "different gsmap size")
    endif

 end subroutine seq_map_gsmapcheck
!===============================================================================

end module seq_map_mod
