// $Id$
//
// Earth System Modeling Framework
// Copyright (c) 2002-2025, University Corporation for Atmospheric Research,
// Massachusetts Institute of Technology, Geophysical Fluid Dynamics
// Laboratory, University of Michigan, National Centers for Environmental
// Prediction, Los Alamos National Laboratory, Argonne National Laboratory,
// NASA Goddard Space Flight Center.
// Licensed under the University of Illinois-NCSA License.
//
//==============================================================================
#define ESMC_FILENAME "ESMCI_Mesh_Regrid_Glue.C"
//==============================================================================
//
// This file contains the Fortran interface code to link F90 and C++.
//
//------------------------------------------------------------------------------
 // INCLUDES
//------------------------------------------------------------------------------
#include "ESMCI_Macros.h"
#include "ESMCI_VM.h"
#include "ESMCI_LogErr.h"
#include "ESMCI_Grid.h"
#include "ESMCI_GridToMesh.h"
#include "ESMC_Util.h"
#include "ESMCI_Array.h"

#include "ESMCI_TraceMacros.h"  // for profiling
#include "ESMCI_TraceRegion.h"  // for profiling

#include "Mesh/include/ESMCI_Mesh.h"
#include "Mesh/include/Legacy/ESMCI_MeshRead.h"
#include "Mesh/include/Regridding/ESMCI_MeshRegrid.h"
#include "Mesh/include/Legacy/ESMCI_Exception.h"
#include "Mesh/include/Regridding/ESMCI_Integrate.h"
#include "Mesh/include/Regridding/ESMCI_Interp.h"
#include "Mesh/include/Regridding/ESMCI_ExtrapolationPoleLGC.h"
#include "Mesh/include/ESMCI_MathUtil.h"
#include "Mesh/include/ESMCI_MathUtil.h"
#include "Mesh/include/Legacy/ESMCI_Phedra.h"
#include "Mesh/include/ESMCI_Mesh_Regrid_Glue.h"
#include "Mesh/include/ESMCI_Mesh_Vector_Regrid.h"
#include "Mesh/include/Legacy/ESMCI_MeshMerge.h"
#include "Mesh/include/ESMCI_Mesh_GToM_Glue.h"


#include <iostream>
#include <vector>
#include <map>

  //------------------------------------------------------------------------------
//BOP
// !DESCRIPTION:
//
//
 //EOP
//-------------------------------------------------------------------------


using namespace ESMCI;


// prototypes from below
static bool all_mesh_node_ids_in_wmat(PointList *pointlist, WMat &wts, int *missing_id);
static bool all_mesh_elem_ids_in_wmat(Mesh *mesh, WMat &wts, int *missing_id);
static bool any_cells_in_mesh_degenerate(Mesh *mesh);
static void get_mesh_node_ids_not_in_wmat(PointList *pointlist, WMat &wts, std::vector<int> *missing_ids);
static void get_mesh_elem_ids_not_in_wmat(Mesh *mesh, WMat &wts, std::vector<int> *missing_ids);
static void change_wts_to_be_fracarea(Mesh *mesh, int num_entries,
                               int *iientries, double *factors);

static void copy_rs_from_WMat_to_Array(WMat *wmat, ESMCI::Array *array);
static void copy_cnsv_rs_from_WMat_to_Array(WMat *wmat, ESMCI::Array *array);

// external C functions
 extern "C" void FTN_X(c_esmc_arraysmmstoreind4)(ESMCI::Array **srcArray,
    ESMCI::Array **dstArray, ESMCI::RouteHandle **routehandle,
    ESMC_TypeKind_Flag *typekind, void *factorList, int *factorListCount,
    ESMCI::InterArray<int> *factorIndexList, ESMC_Logical *ignoreUnmatched,
    int *srcTermProcessing, int *pipelineDepth, int *rc);

void CpMeshDataToArray(Grid &grid, int staggerLoc, ESMCI::Mesh &mesh, ESMCI::Array &array, MEField<> *dataToArray);
void CpMeshElemDataToArray(Grid &grid, int staggerloc, ESMCI::Mesh &mesh, ESMCI::Array &array, MEField<> *dataToArray);
void PutElemAreaIntoArray(Grid &grid, int staggerLoc, ESMCI::Mesh &mesh, ESMCI::Array &array);

void ESMCI_regrid_create(
                     Mesh **meshsrcpp, ESMCI::Array **arraysrcpp, ESMCI::PointList **plsrcpp,
                     Mesh **meshdstpp, ESMCI::Array **arraydstpp, ESMCI::PointList **pldstpp,
                     int *regridMethod,
                     int *map_type,
                     int *norm_type,
                     int *_vectorRegrid, 
                     int *regridPoleType, int *regridPoleNPnts,
                     int *extrapMethod,
                     int *extrapNumSrcPnts,
                     ESMC_R8 *extrapDistExponent,
                     int *extrapNumLevels,
                     int *extrapNumInputLevels, 
                     int *unmappedaction, int *_ignoreDegenerate,
                     int *srcTermProcessing, int *pipelineDepth,
                     ESMCI::RouteHandle **rh, int *has_rh,
                     int *has_iw, int *nentries, ESMCI::TempWeights **tweights,
                     ESMCI::RouteHandle **trh, int *has_trh,
                     int *has_udl, int *_num_udl, ESMCI::TempUDL **_tudl,
                     int *_has_statusArray, ESMCI::Array **_statusArray,
                     int *_checkFlag, 
                     int*rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "ESMCI_regrid_create()"
  Trace __trace(" FTN_X(regrid_test)(ESMCI::Grid **gridsrcpp, ESMCI::Grid **griddstcpp, int*rc");

  // Declare local return code
  int localrc;

  
  // Dereference input variables
  ESMCI::Array &srcarray = **arraysrcpp;
  ESMCI::Array &dstarray = **arraydstpp;

  Mesh *srcmesh = *meshsrcpp;
  Mesh *dstmesh = *meshdstpp;

  PointList *dstpointlist = *pldstpp;
  PointList *srcpointlist = *plsrcpp;

  int has_statusArray=*_has_statusArray;
  ESMCI::Array *statusArray=*_statusArray;

  
#define PROGRESSLOG_off
#define MEMLOG_off

#ifdef PROGRESSLOG_on
  ESMC_LogDefault.Write("c_esmc_regrid_create(): Just entered routine.", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
  VM::logMemInfo(std::string("RegridCreate1.0"));
#endif

  try {

    // Initialize the parallel environment for mesh
    ESMCI::Par::Init("MESHLOG", false, VM::getCurrent(&localrc)->getMpi_c());
    if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;  // bail out with exception
    
    // transalate vectorRegrid to C++ bool
    bool vectorRegrid=false;
    if (*_vectorRegrid == 1) vectorRegrid=true;
    
    // transalate ignoreDegenerate to C++ bool
    bool ignoreDegenerate=false;
    if (*_ignoreDegenerate == 1) ignoreDegenerate=true;

    // transalate checkFlag to C++ bool
    bool checkFlag=false;
    if (*_checkFlag == 1) checkFlag=true;

    
    // Output Warning message about checkFlag
    if (checkFlag){
      ESMC_LogDefault.Write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
                            ESMC_LOGMSG_WARN);
      ESMC_LogDefault.Write("!!! Calling regrid weight generation                  !!!", 
                            ESMC_LOGMSG_WARN);
      ESMC_LogDefault.Write("!!! (e.g. ESMF_FieldRegridStore()) with checkFlag on. !!!", 
                            ESMC_LOGMSG_WARN);
      ESMC_LogDefault.Write("!!! Extra checking comes at the cost of performance.  !!!",
                            ESMC_LOGMSG_WARN);
      ESMC_LogDefault.Write("!!! Only use for debugging, NOT for production!       !!!",
                            ESMC_LOGMSG_WARN);
      ESMC_LogDefault.Write("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
                            ESMC_LOGMSG_WARN);
    }

    ////// Sanity checks /////

    // extrapolation not supported with conservative methods
    if (*extrapMethod != ESMC_EXTRAPMETHOD_NONE) {
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                         "Extrapolation is not currently supported with conservative regrid methods.",
             ESMC_CONTEXT, &localrc)) throw localrc;
      }
    }


    // Conservative not supported on 3D spherical meshes
    if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
        (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
      if ((srcmesh->parametric_dim() == 3) && 
          (dstmesh->parametric_dim() == 3)) {
        if ((srcmesh->coordsys != ESMC_COORDSYS_CART) || 
            (dstmesh->coordsys != ESMC_COORDSYS_CART)) {
          
          if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                           "Conservative regridding isn't supported on 3D spherical Grids or Meshes.",
                                           ESMC_CONTEXT, &localrc)) throw localrc;
        }
      }
    }

    /// vectorRegrid only supported with spherical geometries
    if (vectorRegrid) {

      // Get src coordSys
      ESMC_CoordSys_Flag src_coord_sys=ESMC_COORDSYS_UNINIT;
      if (srcmesh != NULL) src_coord_sys=srcmesh->coordsys;
      else if (srcpointlist != NULL) src_coord_sys=srcpointlist->get_orig_coord_sys();

      // Check that it's spherical 
      if ((src_coord_sys != ESMC_COORDSYS_SPH_DEG) && (src_coord_sys != ESMC_COORDSYS_SPH_RAD)) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                         "Vector regridding currently only supported for source geometries (e.g. Grids) with a spherical coordinate system.",
                                         ESMC_CONTEXT, &localrc)) throw localrc;
      }


      // Get dst coordSys
      ESMC_CoordSys_Flag dst_coord_sys=ESMC_COORDSYS_UNINIT;
      if (dstmesh != NULL) dst_coord_sys=dstmesh->coordsys;
      else if (dstpointlist != NULL) dst_coord_sys=dstpointlist->get_orig_coord_sys();

      // Check that it's spherical 
      if ((dst_coord_sys != ESMC_COORDSYS_SPH_DEG) && (dst_coord_sys != ESMC_COORDSYS_SPH_RAD)) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                         "Vector regridding currently only supported for destination geometries (e.g. Grids) with a spherical coordinate system.",
                                         ESMC_CONTEXT, &localrc)) throw localrc;
      }

    }

    
    /// vectorRegrid currently only supported with 1 ungridded dim
    if (vectorRegrid) {

      // Check src
      if (srcarray.getTensorCount() != 1) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                         "srcField must have exactly 1 ungridded dimension to use vector regridding.",
                                         ESMC_CONTEXT, &localrc)) throw localrc;
      }

      // Check dst
      if (dstarray.getTensorCount() != 1) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                         "dstField must have exactly 1 ungridded dimension to use vector regridding.",
                                         ESMC_CONTEXT, &localrc)) throw localrc;
      }
    }
    
    
     //// Precheck Meshes for errors
    bool degenerate=false;

    // If not ignoring, check for degenerate elements
    if (!ignoreDegenerate) {
      // Check source mesh elements
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND) ||
          (*regridMethod==ESMC_REGRID_METHOD_BILINEAR) ||
          (*regridMethod==ESMC_REGRID_METHOD_PATCH)) {
        degenerate=any_cells_in_mesh_degenerate(srcmesh);
      }

      // Degenerate
      if (degenerate) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
        "- Src contains a cell that has corners close enough that the cell "
        "collapses to a line or point", ESMC_CONTEXT, &localrc)) throw localrc;
      }

      // Only check dst mesh elements for conservative because for others just nodes are used and it doesn't
      // matter what the cell looks like
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
        // Check mesh elements
        degenerate=any_cells_in_mesh_degenerate(dstmesh);

        // Degenerate
        if (degenerate) {
          if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
        "- Dst contains a cell which has corners close enough that the cell "
        "collapses to a line or point", ESMC_CONTEXT, &localrc)) throw localrc;
        }
      }
    }




    // Get vectorRegrid dims
    // TODO: Move to a better place
    int num_vec_dims;
    int src_vec_dims_undist_seqind[ESMF_MAXDIM];
    int dst_vec_dims_undist_seqind[ESMF_MAXDIM];
    if (vectorRegrid) {

      // Get src info
      int src_num_vec_dims;
      get_vec_dims_for_vectorRegrid(srcarray, src_num_vec_dims, src_vec_dims_undist_seqind);

      // Get dst info
      int dst_num_vec_dims;
      get_vec_dims_for_vectorRegrid(dstarray, dst_num_vec_dims, dst_vec_dims_undist_seqind);

      // The size of the dimensions must match
      if (src_num_vec_dims != dst_num_vec_dims) {
        if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                  "The srcField and the dstField must have the same size ungridded dimension to use vector regridding.",
                                         ESMC_CONTEXT, &localrc)) throw localrc;
      }

      // Set num vector dims
      // (Setting to src, but dst should be the same as checked above)
      num_vec_dims=src_num_vec_dims;
    }


    // Enter profile around weight generation
    if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
      ESMCI::TraceEventRegionEnter("Weight generation", &localrc);
      if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
    }
    
#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Entering weight generation.", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate2.0"));
#endif

    // Compute Weights matrix
    IWeights *wts = new IWeights;

    // Turn off unmapped action checking in regrid because it's local to a proc, and can therefore
    // return false positives for multiproc cases, instead check below after gathering weights to a proc.
    int temp_unmappedaction=ESMCI_UNMAPPEDACTION_IGNORE;

    // Setup Destination status
    bool set_dst_status=false;
    if (has_statusArray) {
      set_dst_status=true;
    }
    WMat dst_status;

    ESMCI_REGRID_TRACE_ENTER("NativeMesh Weight Generation");

    // to do NEARESTDTOS just do NEARESTSTOD and invert results
    if (*regridMethod != ESMC_REGRID_METHOD_NEAREST_DST_TO_SRC) {

      if(!regrid(srcmesh, srcpointlist, dstmesh, dstpointlist, 
                 NULL, *wts, 
                 regridMethod, 
                 regridPoleType, regridPoleNPnts,
                 map_type,
                 extrapMethod,
                 extrapNumSrcPnts,
                 extrapDistExponent,
                 extrapNumLevels,
                 extrapNumInputLevels, 
                 &temp_unmappedaction,
                 set_dst_status, dst_status,
                 checkFlag)) {
        Throw() << "Online regridding error" << std::endl;
      }
    } else {
      int tempRegridMethod=ESMC_REGRID_METHOD_NEAREST_SRC_TO_DST;

      if(!regrid(dstmesh, dstpointlist, srcmesh, srcpointlist, 
                 NULL, *wts,
                 &tempRegridMethod, 
                 regridPoleType, regridPoleNPnts,
                  map_type,
                 extrapMethod,
                 extrapNumSrcPnts,
                 extrapDistExponent,
                 extrapNumLevels,
                 extrapNumInputLevels, 
                 &temp_unmappedaction,
                 set_dst_status, dst_status,
                 checkFlag)) {
        Throw() << "Online regridding error" << std::endl;
      }
    }

    // Exit profile around weight generation
    if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
      ESMCI::TraceEventRegionExit("Weight generation", &localrc);
      if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
    }
    
    ESMCI_REGRID_TRACE_EXIT("NativeMesh Weight Generation");

#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Done with weight generation... check unmapped dest,", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate3.0"));
#endif

    // If requested get list of unmapped destination points
    std::vector<int> unmappedDstList;
    if (*has_udl) {
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
        get_mesh_elem_ids_not_in_wmat(dstmesh, *wts, &unmappedDstList);
      } else if (*regridMethod == ESMC_REGRID_METHOD_NEAREST_DST_TO_SRC) {
        // CURRENTLY DOESN'T WORK!!!
#if 0
        get_mesh_node_ids_not_in_wmat(srcmesh, *wts, &unmappedDstList);
#endif

      } else { // Non-conservative
        get_mesh_node_ids_not_in_wmat(dstpointlist, *wts, &unmappedDstList);
      }
    }
#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): More unmapped points checking.", ESMC_LOGMSG_INFO);
#endif

    // If user is worried about unmapped points then check that
    // here, because we have all the dest objects and weights
    // gathered onto the same proc.
    if (*unmappedaction==ESMCI_UNMAPPEDACTION_ERROR) {
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
        int missing_id;
        if (!all_mesh_elem_ids_in_wmat(dstmesh, *wts, &missing_id)) {
          char msg[1024];
          sprintf(msg,"- There exist destination cells (e.g. id=%d) which don't overlap with any "
            "source cell",missing_id);
          if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_INCOMP, msg,
             ESMC_CONTEXT, &localrc)) throw localrc;
        }
      } else if (*regridMethod == ESMC_REGRID_METHOD_NEAREST_DST_TO_SRC) {
        // CURRENTLY DOESN'T WORK!!!
#if 0
        if (!all_mesh_node_ids_in_wmat(srcmesh, *wts)) {
          if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_INCOMP,
            "- There exist source points which can't be mapped to any "
            "destination point", ESMC_CONTEXT, &localrc)) throw localrc;
        }
#endif

      } else { // bilinear, patch, ...
        int missing_id;

        if (!all_mesh_node_ids_in_wmat(dstpointlist, *wts, &missing_id)) {
          char msg[1024];
          sprintf(msg,"- There exist destination points (e.g. id=%d) which can't be mapped to any "
            "source cell",missing_id);
          if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_INCOMP, msg,
             ESMC_CONTEXT, &localrc)) throw localrc;
        }
      }
    }
#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Prepare for ArraySMMStore().", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate4.0"));
#endif

    
    /////// We have the weights, now set up the sparsemm object /////

    // Get the size of the sparse matrix and allocate space for it
    std::pair<UInt,UInt> iisize = wts->count_matrix_entries();
    int num_entries = iisize.first;
    int iientries_entry_size=2;
    int *iientries = new int[2*iisize.first];
    double *factors = new double[iisize.first];

    

    // Translate weights to sparse matrix representation
    if (*regridMethod != ESMC_REGRID_METHOD_NEAREST_DST_TO_SRC) {
      UInt i = 0;
      WMat::WeightMap::iterator wi = wts->begin_row(), we = wts->end_row();
      for (; wi != we; ++wi) {
        const WMat::Entry &w = wi->first;

        std::vector<WMat::Entry> &wcol = wi->second;

        // Construct factor index list
        for (UInt j = 0; j < wcol.size(); ++j) {
          UInt twoi = 2*i;
          const WMat::Entry &wc = wcol[j];

          // Construct factor list entry
          iientries[twoi+1] = w.id;  iientries[twoi] = wc.id;
          factors[i] = wc.value;

#define ESMF_REGRID_DEBUG_OUTPUT_WTS_ALL_off

#ifdef ESMF_REGRID_DEBUG_OUTPUT_WTS_ALL
          printf("d_id=%d  s_id=%d s=%d w=%20.17E \n",w.id,wc.id,wc.src_id,wc.value);
#endif
#ifdef ESMF_REGRID_DEBUG_OUTPUT_WTS_SID
          if (wc.id==ESMF_REGRID_DEBUG_OUTPUT_WTS_SID) {
             printf("d_id=%d  s_id=%d w=%20.17E \n",w.id,wc.id,wc.value);
          }
#endif
#ifdef ESMF_REGRID_DEBUG_OUTPUT_WTS_DID
          if (w.id==ESMF_REGRID_DEBUG_OUTPUT_WTS_DID) {
            printf("d_id=%d  s_id=%d s=%d w=%20.17E \n",w.id,wc.id,wc.src_id,wc.value);
          }
#endif

          i++;
        } // for j
      } // for wi

    } else {
      UInt i = 0;
      WMat::WeightMap::iterator wi = wts->begin_row(), we = wts->end_row();
      for (; wi != we; ++wi) {
        const WMat::Entry &w = wi->first;

        std::vector<WMat::Entry> &wcol = wi->second;

        // Construct factor index list
        for (UInt j = 0; j < wcol.size(); ++j) {
          UInt twoi = 2*i;
          const WMat::Entry &wc = wcol[j];

          // Construct factor list entry
          // INVERT SRC and DST ID
          iientries[twoi+1] = wc.id;  iientries[twoi] = w.id;
          factors[i] = wc.value;

          // printf("d_id=%d  s_id=%d w=%f \n",w.id,wc.id,wc.value);

          i++;
        } // for j
      } // for wi
    }

    ///// If conservative, translate split element weights to non-split //////
    if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
        (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
      if (srcmesh->is_split) translate_split_src_elems_in_wts(srcmesh, num_entries, iientries);
      if (dstmesh->is_split) translate_split_dst_elems_in_wts(dstmesh, num_entries, iientries, factors);
    }


    ///// If conservative then modify weights according to norm type //////
    if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
        (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
      if (*norm_type==ESMC_NORM_TYPE_FRACAREA) change_wts_to_be_fracarea(dstmesh, num_entries, iientries, factors);
    }


    ///// If requested, then change weights to be vector weights
    if (vectorRegrid) {

      // Declare vector Matrix
      int num_entries_vec;
      int *iientries_vec;
      double *factors_vec;

      // Create vector weights from regular weights
      create_vector_sparse_mat_from_reg_sparse_mat(num_entries, iientries, factors,
                                                    num_vec_dims, src_vec_dims_undist_seqind, dst_vec_dims_undist_seqind,
                                                    srcmesh, srcpointlist,
                                                    dstmesh, dstpointlist,
                                                    num_entries_vec, iientries_vec, factors_vec);

      // Get rid of old matrix
      delete [] factors;
      delete [] iientries;
      num_entries = 0;      
      
      // Swap matrix to vector version
      num_entries=num_entries_vec;
      iientries=iientries_vec;
      factors=factors_vec;
      iientries_entry_size=4;  // Because we have the ungridded dims entries the matrix is now of size 4
    }


    
    // Copy status info from WMat to Array
    if (has_statusArray) {
      if ((*regridMethod==ESMC_REGRID_METHOD_CONSERVE) ||
          (*regridMethod==ESMC_REGRID_METHOD_CONSERVE_2ND)) {
        copy_cnsv_rs_from_WMat_to_Array(&dst_status, statusArray);
      } else {
        copy_rs_from_WMat_to_Array(&dst_status, statusArray);
      }
    }

#ifdef PROGRESSLOG_on
    char msgString[1024];
    sprintf(msgString, "c_esmc_regrid_create(): num_entries=%d.", num_entries);
    ESMC_LogDefault.Write(msgString, ESMC_LOGMSG_INFO);
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Entering ArraySMMStore().", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate5.0"));
#endif

    delete wts; // local garbage collection

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate5.1"));
#endif


   
#ifdef C_SIDE_REGRID_FREED_MESH
    // enabling this freature currently breaks several tests
    delete srcmesh;
    delete dstmesh;
    //TODO: also drop PointList objects here if possible to reduce Store() memory footrint
#endif


    
    //// Creation of routeHandle ////
    
#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate5.2"));
#endif

    ESMCI_REGRID_TRACE_ENTER("NativeMesh ArraySMMStore");

    // Build the RouteHandle using ArraySMMStore() 
    if (*has_rh) {

      // Enter profile around routehandle creation
      if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
        ESMCI::TraceEventRegionEnter("RouteHandle creation", &localrc);
        if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
      }

      // Set some flags
      enum ESMC_TypeKind_Flag tk = ESMC_TYPEKIND_R8;
      ESMC_Logical ignoreUnmatched = ESMF_FALSE;

      // Wrap factorIndexList in InterArray
      int larg[2] = {iientries_entry_size, num_entries};
      ESMCI::InterArray<int> ii(iientries, 2, larg);
      ESMCI::InterArray<int> *iiptr = &ii;
      
      // Call into Array sparse matrix multiply store to create RouteHandle
      FTN_X(c_esmc_arraysmmstoreind4)(arraysrcpp, arraydstpp, rh, &tk, factors,
            &num_entries, iiptr, &ignoreUnmatched, srcTermProcessing,
            pipelineDepth, &localrc);
      if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
        ESMC_CONTEXT, NULL)) throw localrc;  // bail out with exception


      // Exit profile around routehandle creation
      if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
        ESMCI::TraceEventRegionExit("RouteHandle creation", &localrc);
        if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
      }
    }

    ESMCI_REGRID_TRACE_EXIT("NativeMesh ArraySMMStore");

#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Returned from ArraySMMStore().", ESMC_LOGMSG_INFO);
#endif


    
    //// Creation of transpose routeHandle ////

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate6.0"));
#endif
    
    
    // If requested, build the transpose RouteHandle using ArraySMMStore() 
    if (*has_trh) {

      ESMCI_REGRID_TRACE_ENTER("NativeMesh Transpose ArraySMMStore");
      
      // Enter profile around routehandle creation
      if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
        ESMCI::TraceEventRegionEnter("Transpose RouteHandle creation", &localrc);
        if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
      }

      // Allocate transpose matrix
      int *transpose_iientries = new int[iientries_entry_size*num_entries];
      
      // Init to beginning entries of factor index lists
      int *entry=iientries;
      int *t_entry=transpose_iientries;
      
      // Depending on size of matrix entries, loop constructing transpose matrix
      if (iientries_entry_size == 2) {
        
        // Loop through entries
        for (int i=0; i<num_entries; i++) {
          
          // Swap src and dst values
          t_entry[0]=entry[1];
          t_entry[1]=entry[0];
          
          // Next entry
          entry += 2;
          t_entry += 2;
        }
      } else if (iientries_entry_size == 4) {

        // Loop through entries
        for (int i=0; i<num_entries; i++) {
          
          // Swap src and dst values
          t_entry[0]=entry[2];
          t_entry[1]=entry[3];
          t_entry[2]=entry[0];
          t_entry[3]=entry[1];
          
          // Next entry
          entry += 4;
          t_entry += 4;
        }
      } else {
        Throw() << "Unsupported tuple size of "<<iientries_entry_size<<" in weight matrix.";
      } 
      
      
      // Set some flags
      enum ESMC_TypeKind_Flag tk = ESMC_TYPEKIND_R8;
      ESMC_Logical ignoreUnmatched = ESMF_FALSE; // TODO: Maybe for transpose this should be true? 

      // Wrap factorIndexList in InterArray
      int transpose_larg[2] = {iientries_entry_size, num_entries};
      ESMCI::InterArray<int> transpose_ii(transpose_iientries, 2, transpose_larg);
      
      // Call into Array sparse matrix multiply store to create RouteHandle
      FTN_X(c_esmc_arraysmmstoreind4)(arraydstpp, arraysrcpp, trh, &tk, factors,
                                      &num_entries, &transpose_ii, &ignoreUnmatched,
                                      srcTermProcessing, pipelineDepth, &localrc);
      if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
                                        ESMC_CONTEXT, NULL)) throw localrc;  // bail out with exception

      // Get rid of transposed factor index list
      delete [] transpose_iientries;

      // Exit profile around routehandle creation
      if (TraceGetProfileTypeInfo(ESMC_PROFILETYPE_REGRID) > 1) {
        ESMCI::TraceEventRegionExit("Transpose RouteHandle creation", &localrc);
        if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL)) throw localrc;
      }

      ESMCI_REGRID_TRACE_EXIT("NativeMesh Transpose ArraySMMStore");
      
#ifdef PROGRESSLOG_on
    ESMC_LogDefault.Write("c_esmc_regrid_create(): Returned from transpose ArraySMMStore().", ESMC_LOGMSG_INFO);
#endif

#ifdef MEMLOG_on
    VM::logMemInfo(std::string("RegridCreate7.0"));
#endif      
    }


    
    //// Output of weight matrix ////

    // If user has requested weights, then save them
    if (*has_iw) {
      // Record the number of entries
      *nentries = num_entries;
      
      // Save off the weights so the F90 caller can allocate arrays and
      // copy the values.
      if (num_entries>0) {
        *tweights = new ESMCI::TempWeights;
        (*tweights)->nentries = num_entries;
        (*tweights)->factors = factors;
        (*tweights)->iientries = iientries;
      } else {
        // No weights, so don't allocate structure
        // Make sure copying method below takes this into account
        *tweights = NULL;
      }

    } else {  //...else get rid of them
      *nentries = 0;
      delete [] factors;
      delete [] iientries;
    }


    //// Handle output of unmapped destination list ////

    // Init info for transferring list
    *_num_udl=0;
    *_tudl=NULL;

    // If user wants umappedDstList, then setup structure to transfer it
    if (*has_udl) {
      // Get number of unmapped points
      int num_udl=unmappedDstList.size();

      // If list entries exist, allocate and fill udl struct
      ESMCI::TempUDL *tudl = NULL;
      if (num_udl > 0) {
        tudl = new ESMCI::TempUDL;
        tudl->udl = NULL;
        tudl->udl = new int[num_udl];
        for (int i=0; i<num_udl; i++) {
          tudl->udl[i]=unmappedDstList[i];
        }
      }

      // Output information
      *_num_udl=num_udl;
      *_tudl=tudl;
    }

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                                  "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
      ESMC_CONTEXT, rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

#ifdef PROGRESSLOG_on
  ESMC_LogDefault.Write("c_esmc_regrid_create(): Final return.", ESMC_LOGMSG_INFO);
#endif

  
  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;
}

void ESMCI_regrid_getiwts(Grid **gridpp,
                   Mesh **meshpp, ESMCI::Array **arraypp, int *staggerLoc,
                   int *rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_regrid_getiwts()"
  Trace __trace(" FTN_X(regrid_getiwts)()");
  ESMCI::Array &array = **arraypp;

  Mesh &mesh = **meshpp;
  Grid &grid = **gridpp;

  try {

    // Get the integration weights
    MEField<> *iwts = mesh.GetField("iwts");
    if (!iwts) Throw() << "Could not find integration weights field on this mesh"
                             <<std::endl;

    if(!get_iwts(mesh, iwts))
      Throw() << "Online regridding error" << std::endl;

    CpMeshDataToArray(grid, *staggerLoc, mesh, array, iwts);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT,
      rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}

void ESMCI_regrid_getarea(Grid **gridpp,
                   Mesh **meshpp, ESMCI::Array **arraypp, int *staggerLoc,
                   int *rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_regrid_getarea()"
  Trace __trace(" FTN_X(regrid_getarea)()");
  ESMCI::Array &array = **arraypp;

  Mesh &mesh = **meshpp;
  Grid &grid = **gridpp;

  try {

    PutElemAreaIntoArray(grid, *staggerLoc, mesh, array);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT,
      rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}



// Assumes array is center stagger loc
void ESMCI_regrid_getfrac(Grid **gridpp,
                   Mesh **meshpp, ESMCI::Array **arraypp, int *staggerLoc,
                   int *rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_regrid_getfrac()"
  Trace __trace(" FTN_X(regrid_getfrac)()");

  ESMCI::Array &array = **arraypp;
  Mesh &mesh = **meshpp;
  Grid &grid = **gridpp;

  try {


    // Get the integration weights
    MEField<> *frac = mesh.GetField("elem_frac");
    if (!frac) Throw() << "Could not find elem_frac field on this mesh"
                             <<std::endl;

    CpMeshElemDataToArray(grid, *staggerLoc, mesh, array, frac);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT,
      rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}


// Get the list of ids in the mesh, but not in the wts
// (i.e. if mesh is the dest. mesh, the unmapped points)
static void get_mesh_node_ids_not_in_wmat(PointList *pointlist, WMat &wts, std::vector<int> *missing_ids) {

  // Get weight iterators
  WMat::WeightMap::iterator wi =wts.begin_row(),we = wts.end_row();

  wi=wts.begin_row();
  int id;
  int curr_num_pts = pointlist->get_curr_num_pts();
  // Loop checking that all nodes have weights
  for (int i=0; i<curr_num_pts; i++) {

    // Skip non local nodes
    // ??

    // get node id
    id = pointlist->get_id(i);

    // get weight id
    int wt_id=wi->first.id;

    // Advance weights until not less than node id
    while ((wi != we) && (wi->first.id < id)) {
      wi++;
    }

    // If teh current weight is not equal to the node id, then we must have passed it, so add it to the list
    if (wi->first.id != id) {
      missing_ids->push_back(id);
    }
  }

}


// Get the list of ids in the mesh, but not in the wts
// (i.e. if mesh is the dest. mesh, the unmapped points)
static void get_mesh_elem_ids_not_in_wmat(Mesh *mesh, WMat &wts, std::vector<int> *missing_ids) {

  // Get mask Field
  MEField<> *mptr = mesh->GetField("elem_mask");

  // Get weight iterators
  WMat::WeightMap::iterator wi =wts.begin_row(),we = wts.end_row();

  // Get mesh node iterator that goes through in order of id
  Mesh::MeshObjIDMap::const_iterator ei=mesh->map_begin(MeshObj::ELEMENT), ee=mesh->map_end(MeshObj::ELEMENT);

  // Loop checking that all nodes have weights
  for (; ei != ee; ++ei) {
    const MeshObj &elem=*ei;

    // Skip non local nodes
    if (!GetAttr(elem).is_locally_owned()) continue;

    // Skip masked elements
    if (mptr != NULL) {
      double *m=mptr->data(elem);
      if (*m > 0.5) continue;
    }

    // get node id
    int elem_id=elem.get_id();

    // get weight id
    int wt_id=wi->first.id;

    // Advance weights until not less than node id
    while ((wi != we) && (wi->first.id <elem_id)) {
      wi++;
    }

    // If teh current weight is not equal to the node id, then we must have passed it, so add it to the list
    if (wi->first.id != elem_id) {
      missing_ids->push_back(elem_id);
    }
  }
}



bool all_mesh_node_ids_in_wmat(PointList *pointlist, WMat &wts, int *missing_id) {

  // Get weight iterators
  WMat::WeightMap::iterator wi =wts.begin_row(),we = wts.end_row();

  wi=wts.begin_row();
  int id;
  int curr_num_pts = pointlist->get_curr_num_pts();

  for (int i=0; i<curr_num_pts; i++) {

    // Skip non local nodes
    // ??

    // get node id
    id = pointlist->get_id(i);

    // get weight id
    int wt_id=wi->first.id;

    // Advance weights until not less than node id
    while ((wi != we) && (wi->first.id < id)) {
      wi++;
    }

    // If we're at the end of the weights then exit saying we don't have
    // all of them
    if (wi==we) {
      *missing_id=id;
      char msg[1024];
      sprintf(msg,"Destination id=%d NOT found in weight matrix.",id);
      ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
      return false;
    }

    // If we're not equal to the node id then we must have passed it
    if (wi->first.id != id) {
      *missing_id=id;
      char msg[1024];
      sprintf(msg,"Destination id=%d NOT found in weight matrix.",id);
      ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
      return false;
    }

  }


  // Still here, so must have found them all
  return true;

}

bool all_mesh_elem_ids_in_wmat(Mesh *mesh, WMat &wts, int *_missing_id) {

  // Get mask Field
  MEField<> *mptr = mesh->GetField("elem_mask");

  // Get weight iterators
  WMat::WeightMap::iterator wi =wts.begin_row(),we = wts.end_row();

  // Get mesh node iterator that goes through in order of id
  Mesh::MeshObjIDMap::const_iterator ei=mesh->map_begin(MeshObj::ELEMENT), ee=mesh->map_end(MeshObj::ELEMENT);

  // Loop checking that all elems have weights
  bool missing=false;
  int missing_id=-1;
  for (; ei != ee; ++ei) {
    const MeshObj &elem=*ei;

    // Skip non local elems
    if (!GetAttr(elem).is_locally_owned()) continue;

    // Skip masked elements
    if (mptr != NULL) {
      double *m=mptr->data(elem);
      if (*m > 0.5) continue;
    }

    // get elem id
    int elem_id=elem.get_id();

    // If we've entered the range of split elements then just quit.
    // (Elements are in order by id here and all the split elems are at top.
    // If we've reached this point then part of the split elem mapped
    // (the part with the orig_id), so no use checking the rest.)
    if (mesh->is_split && (elem_id > mesh->max_non_split_id)) break;

    // Advance weights until not less than elem id
    while ((wi != we) && (wi->first.id <elem_id)) {
      wi++;
    }

    // If we're at the end of the weights then exit saying we don't have
    // all of them
    if (wi==we) {
      missing=true;
      missing_id=elem_id;
      break;
    }

    // If we're not equal to the elem id then we must have passed it
    if (wi->first.id != elem_id) {
      missing=true;
      missing_id=elem_id;
      break;
    }
  }

  // If not missing leave
  if (!missing) return true;

  // If the mesh contains split elements, we could have
  // a false missing element, so only leave if we're not split
  if (missing && !mesh->is_split) {
    *_missing_id=missing_id;
    char msg[1024];
    sprintf(msg,"Destination id=%d NOT found in weight matrix.",missing_id);
    ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
    return false;
  }

  //
  // We should only reach this point if we are split and have missing elements //
  //

  // Count the number of entries in the list
  int num_dst_ids=0;
  for (wi = wts.begin_row(); wi != we; ++wi) {
    num_dst_ids++;
  }

  // If there are no dst ids, then leave
  if (num_dst_ids <= 0) return true;

  // Allocate space for ids
  UInt *dst_ids = NULL;
  if (num_dst_ids>0) dst_ids=new UInt[num_dst_ids];

  // Loop through weights generating a list of destination ids
  int pos=0;
  for (wi = wts.begin_row(); wi != we; ++wi) {
    const WMat::Entry &w = wi->first;

    // Get original id
    UInt orig_id;
    std::map<UInt,UInt>::iterator soi =  mesh->split_to_orig_id.find(w.id);
    if (soi == mesh->split_to_orig_id.end()) {
      orig_id=w.id;
    } else {
      orig_id=soi->second;
    }

    // Put in array
    dst_ids[pos]=orig_id;

    // Next position
    pos++;
  }

  // Sort list
  std::sort(dst_ids,dst_ids+num_dst_ids);

  // Loop checking that all elems have weights with new sorted list
  missing=false;
  missing_id=-1;
  pos=0;
  for (ei=mesh->map_begin(MeshObj::ELEMENT); ei != ee; ++ei) {
    const MeshObj &elem=*ei;

    // Skip non local elems
    if (!GetAttr(elem).is_locally_owned()) continue;

    // Skip masked elements
    if (mptr != NULL) {
      double *m=mptr->data(elem);
      if (*m > 0.5) continue;
    }

    // get elem id
    int elem_id=elem.get_id();

    // If we've entered the range of split elements then just quit.
    // (Elements are in order by id here and all the split elems are at top.
    // If we've reached this point then part of the split elem mapped
    // (the part with the orig_id), so no use checking the rest.)
    if (mesh->is_split && (elem_id > mesh->max_non_split_id)) break;

    // Advance weights until not less than elem id
    while ((pos<num_dst_ids) && (dst_ids[pos] < elem_id)) {
      pos++;
    }

    // If we're at the end of the weights then exit saying we don't have
    // all of them
    if (pos >= num_dst_ids) {
      missing=true;
      missing_id=elem_id;
      break;
    }

    // If we're not equal to the elem id then we must have passed it
    if (dst_ids[pos] != elem_id) {
      missing=true;
      missing_id=elem_id;
      break;
    }
  }

  // deallocate list
  if (dst_ids !=NULL) delete [] dst_ids;

  // If the mesh contains split elements, we could have
  // a false missing element, so only leave if we're not split
  if (missing) {
    *_missing_id=missing_id;
    char msg[1024];
    sprintf(msg,"Destination id=%d NOT found in weight matrix.",missing_id);
    ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
    return false;
  }

  // Still here, so must have found them all
  return true;
}




// OLD VERSION, BUT NOW WE SUPPORT clockwise and concave
#undef  ESMC_METHOD
#define ESMC_METHOD "any_cells_in_mesh_degenerate(Mesh &mesh)"
static bool any_cells_in_mesh_degenerate(Mesh *meshp) {

  // Declare polygon information
#define  MAX_NUM_POLY_COORDS  60
#define  MAX_NUM_POLY_NODES_2D  30  // MAX_NUM_POLY_COORDS/2
#define  MAX_NUM_POLY_NODES_3D  20  // MAX_NUM_POLY_COORDS/3
  int num_poly_nodes;
  double poly_coords[MAX_NUM_POLY_COORDS];

  int num_poly_nodes_orig;
  double poly_coords_orig[MAX_NUM_POLY_COORDS];

  // Translate to mesh
  Mesh &mesh=*meshp;

  // Get coord field
  MEField<> *cfield = mesh.GetCoordField();

  // Get mask Field
  MEField<> *mptr = mesh.GetField("elem_mask");

  // Get dimensions
  int sdim=mesh.spatial_dim();
  int pdim=mesh.parametric_dim();

  // Compute area depending on dimensions
  if (pdim==2) {
    if (sdim==2) {
      MeshDB::const_iterator ei = mesh.elem_begin(), ee = mesh.elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }

        // Init. Degenerate
        bool is_degenerate=false;

        // Get the coords
        get_elem_coords(&elem, cfield, 2, MAX_NUM_POLY_NODES_2D, &num_poly_nodes, poly_coords);

        // Save original coords
        std::copy(poly_coords,poly_coords+2*num_poly_nodes,poly_coords_orig);
        num_poly_nodes_orig=num_poly_nodes;

        // Get rid of 0 len edges
        remove_0len_edges2D(&num_poly_nodes, poly_coords);

         // If less than 3 nodes then is degenerate
        if (num_poly_nodes <3) is_degenerate=true;

        // If is smashed quad then is degenerate
        if (is_smashed_quad2D(num_poly_nodes, poly_coords)) is_degenerate=true;

        // Check if degenerate
        if (is_degenerate) {
             char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~ Degenerate Element Detected ~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  degenerate elem. id=%ld",elem.get_id());
             ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  degenerate elem. coords ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  --------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
              double *pnt=poly_coords_orig+2*i;

              sprintf(msg,"    %d  (%f,  %f) ",i,pnt[0],pnt[1]);
              ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            return true;
        }
      }
    } else if (sdim==3) {
      MeshDB::const_iterator ei = mesh.elem_begin(), ee = mesh.elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }


        // Init. Degenerate
        bool is_degenerate=false;

        // Get the coords
        get_elem_coords(&elem, cfield, 3, MAX_NUM_POLY_NODES_3D, &num_poly_nodes, poly_coords);

         // Save original coords
        std::copy(poly_coords,poly_coords+3*num_poly_nodes,poly_coords_orig);
        num_poly_nodes_orig=num_poly_nodes;


        // Get rid of 0 len edges
        remove_0len_edges3D(&num_poly_nodes, poly_coords);


        // If less than 3 nodes then is degenerate
        if (num_poly_nodes <3) is_degenerate=true;


         // If is smashed quad then is degenerate
        if (is_smashed_quad3D(num_poly_nodes, poly_coords)) is_degenerate=true;


        // Check if degenerate
        if (is_degenerate) {
            char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~ Degenerate Element Detected ~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  degenerate elem. id=%ld",elem.get_id());
            ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  degenerate elem. coords (lon [-180 to 180], lat [-90 to 90]) (x,y,z)",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ----------------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
              double *pnt=poly_coords_orig+3*i;

              double lon, lat, r;
               convert_cart_to_sph_deg(pnt[0], pnt[1], pnt[2],
                                       &lon, &lat, &r);

              sprintf(msg,"    %d  (%f,  %f)  (%f, %f, %f)",i,lon,lat,pnt[0],pnt[1],pnt[2]);
              ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            return true;
        }
      }
    }
  }

  // TODO: Check to see if 3D elements are in correct order.

  // output no degenerate elems found
  return false;
}

// OLD VERSION, BUT NOW WE SUPPORT clockwise and concave
#undef  ESMC_METHOD
#define ESMC_METHOD "cnsrv_check_for_mesh_errors()"
static void cnsrv_check_for_mesh_errors(Mesh &mesh, bool ignore_degenerate, bool *concave, bool *clockwise, bool *degenerate) {

  // Declare polygon information
#define  MAX_NUM_POLY_COORDS  60
#define  MAX_NUM_POLY_NODES_2D  30  // MAX_NUM_POLY_COORDS/2
#define  MAX_NUM_POLY_NODES_3D  20  // MAX_NUM_POLY_COORDS/3
  int num_poly_nodes;
  double poly_coords[MAX_NUM_POLY_COORDS];

  int num_poly_nodes_orig;
  double poly_coords_orig[MAX_NUM_POLY_COORDS];

  // Init variables
  *concave=false;
  *clockwise=false;
  *degenerate=false;

  // Get coord field
  MEField<> *cfield = mesh.GetCoordField();

  // Get mask Field
  MEField<> *mptr = mesh.GetField("elem_mask");

  // Get dimensions
  int sdim=mesh.spatial_dim();
   int pdim=mesh.parametric_dim();

  // Compute area depending on dimensions
  if (pdim==2) {
    if (sdim==2) {
      MeshDB::const_iterator ei = mesh.elem_begin(), ee = mesh.elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }

        // Init. Degenerate
        bool is_degenerate=false;

        // Get the coords
        get_elem_coords(&elem, cfield, 2, MAX_NUM_POLY_NODES_2D, &num_poly_nodes, poly_coords);

        // Save original coords
        std::copy(poly_coords,poly_coords+2*num_poly_nodes,poly_coords_orig);
        num_poly_nodes_orig=num_poly_nodes;

        // Get rid of 0 len edges
        remove_0len_edges2D(&num_poly_nodes, poly_coords);

         // If less than 3 nodes then is degenerate
        if (num_poly_nodes <3) is_degenerate=true;

        // If is smashed quad then is degenerate
        if (is_smashed_quad2D(num_poly_nodes, poly_coords)) is_degenerate=true;

        // Check if degenerate
        if (is_degenerate) {
          if (ignore_degenerate) {
            continue;
          } else {
             char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~ Degenerate Element Detected ~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  degenerate elem. id=%ld",elem.get_id());
             ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  degenerate elem. coords ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  --------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
              double *pnt=poly_coords_orig+2*i;

              sprintf(msg,"    %d  (%f,  %f) ",i,pnt[0],pnt[1]);
              ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            *degenerate=true;
            return;
          }
        }

        //// WE CAN NOW HANDLE CONCAVE CELLS, SO DON'T CHECK THIS ////
#if 0
        // Get elem rotation
        bool left_turn;
        bool right_turn;
        rot_2D_2D_cart(num_poly_nodes, poly_coords, &left_turn, &right_turn);

        // Look for errors
        if (right_turn) {
          if (left_turn) {
            char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~ Concave Element Detected ~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  concave elem. id=%ld",elem.get_id());
            ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  concave elem. coords ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  --------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
               double *pnt=poly_coords_orig+2*i;

              sprintf(msg,"    %d  (%f,  %f) ",i,pnt[0],pnt[1]);
               ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
             ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            *concave=true;
            return;
          }
        }
#endif
      }
    } else if (sdim==3) {
      MeshDB::const_iterator ei = mesh.elem_begin(), ee = mesh.elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }


        // Init. Degenerate
        bool is_degenerate=false;

        // Get the coords
        get_elem_coords(&elem, cfield, 3, MAX_NUM_POLY_NODES_3D, &num_poly_nodes, poly_coords);

         // Save original coords
        std::copy(poly_coords,poly_coords+3*num_poly_nodes,poly_coords_orig);
        num_poly_nodes_orig=num_poly_nodes;


        // Get rid of 0 len edges
        remove_0len_edges3D(&num_poly_nodes, poly_coords);


        // If less than 3 nodes then is degenerate
        if (num_poly_nodes <3) is_degenerate=true;


         // If is smashed quad then is degenerate
        if (is_smashed_quad3D(num_poly_nodes, poly_coords)) is_degenerate=true;


        // Check if degenerate
        if (is_degenerate) {
           if (ignore_degenerate) {
            continue;
          } else {
            char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~ Degenerate Element Detected ~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  degenerate elem. id=%ld",elem.get_id());
            ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  degenerate elem. coords (lon [-180 to 180], lat [-90 to 90]) (x,y,z)",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ----------------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
              double *pnt=poly_coords_orig+3*i;

              double lon, lat, r;
               convert_cart_to_sph_deg(pnt[0], pnt[1], pnt[2],
                                       &lon, &lat, &r);

              sprintf(msg,"    %d  (%f,  %f)  (%f, %f, %f)",i,lon,lat,pnt[0],pnt[1],pnt[2]);
              ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            *degenerate=true;
            return;
          }
        }

        //// WE CAN NOW HANDLE CONCAVE CELLS, SO DON'T CHECK THIS ////
#if 0
        // Get elem rotation
        bool left_turn;
        bool right_turn;
        rot_2D_3D_sph(num_poly_nodes, poly_coords, &left_turn, &right_turn);

        // Look for errors
        if (right_turn) {
          if (left_turn) {
            char msg[1024];
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~ Concave Element Detected ~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);
            sprintf(msg,"  concave elem. id=%ld",elem.get_id());
            ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  concave elem. coords (lon [-180 to 180], lat [-90 to 90]) (x,y,z)",ESMC_LOGMSG_ERROR);
            ESMC_LogDefault.Write("  ----------------------------------------------------------------- ",ESMC_LOGMSG_ERROR);
            for(int i=0; i< num_poly_nodes_orig; i++) {
              double *pnt=poly_coords_orig+3*i;

              double lon, lat, r;
              convert_cart_to_sph_deg(pnt[0], pnt[1], pnt[2],
                                      &lon, &lat, &r);

              sprintf(msg,"    %d  (%f,  %f)  (%f, %f, %f)",i,lon,lat,pnt[0],pnt[1],pnt[2]);
               ESMC_LogDefault.Write(msg,ESMC_LOGMSG_ERROR);
            }
            ESMC_LogDefault.Write("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",ESMC_LOGMSG_ERROR);

            *concave=true;
            return;
          }
        }
#endif
      }
    }
  }


  // TODO: Check to see if 3D elements are in correct order.

}

#if 0
static void noncnsrv_check_for_mesh_errors(Mesh *mesh, bool ignore_degenerate, bool *concave, bool *clockwise, bool *degenerate) {

  // Declare polygon information
#define  MAX_NUM_POLY_COORDS  60
#define  MAX_NUM_POLY_NODES_2D  30  // MAX_NUM_POLY_COORDS/2
#define  MAX_NUM_POLY_NODES_3D  20  // MAX_NUM_POLY_COORDS/3
  int num_poly_nodes;
  double poly_coords[MAX_NUM_POLY_COORDS];

  // Init variables
  *concave=false;
  *clockwise=false;
  *degenerate=false;

  // Get coord field
  MEField<> *cfield = mesh->GetCoordField();

  // Get mask Field
  MEField<> *mptr = mesh->GetField("mask");

  // Get dimensions
  int sdim=mesh->spatial_dim();
  int pdim=mesh->parametric_dim();

  // Compute area depending on dimensions
  if (pdim==2) {
    if (sdim==2) {
      MeshDB::const_iterator ei = mesh->elem_begin(), ee = mesh->elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;


        /// NEED TO CHANGE THIS TO WORK WITH ELEMENT NODES (OR MAKE A FUNCTION?)
        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }

        // Get the coords
        get_elem_coords(&elem, cfield, 2, MAX_NUM_POLY_NODES_2D, &num_poly_nodes, poly_coords);

        // Get rid of 0 len edges
        remove_0len_edges2D(&num_poly_nodes, poly_coords);

        // Check if degenerate
        if (num_poly_nodes <3) {
          if (ignore_degenerate) {
            continue;
          } else {
            *degenerate=true;
            return;
          }
        }

        // Get elem rotation
        bool left_turn;
        bool right_turn;
        rot_2D_2D_cart(num_poly_nodes, poly_coords, &left_turn, &right_turn);

        // Look for errors
        if (right_turn) {
          if (left_turn) {
            *concave=true;
            return;
          } else {
            *clockwise=true;
            return;
          }
        }
      }
    } else if (sdim==3) {
      MeshDB::const_iterator ei = mesh->elem_begin(), ee = mesh->elem_end();
      for (; ei != ee; ++ei) {
        // Get the element
        const MeshObj &elem = *ei;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

       /// NEED TO CHANGE THIS TO WORK WITH ELEMENT NODES (OR MAKE A FUNCTION?)
        // Skip masked elements
        if (mptr != NULL) {
          double *m=mptr->data(elem);
          if (*m > 0.5) continue;
        }

        // Get the coords
        get_elem_coords(&elem, cfield, 3, MAX_NUM_POLY_NODES_3D, &num_poly_nodes, poly_coords);

        // Get rid of 0 len edges
        remove_0len_edges3D(&num_poly_nodes, poly_coords);

        // Check if degenerate
        if (num_poly_nodes <3) {
          if (ignore_degenerate) {
            continue;
          } else {
            *degenerate=true;
            return;
          }
        }

        // Get elem rotation
        bool left_turn;
        bool right_turn;
        rot_2D_3D_sph(num_poly_nodes, poly_coords, &left_turn, &right_turn);

        // Look for errors
        if (right_turn) {
          if (left_turn) {
            *concave=true;
            return;
          } else {
            *clockwise=true;
            printf(" clockwise element=%d\n",elem.get_id());
            printf(" num nodes=%d\n",num_poly_nodes);
            write_3D_poly_woid_to_vtk("clockwise", num_poly_nodes, poly_coords);
           return;
          }
        }
      }
    }
  }

  // TODO: Check to see if 3D elements are in correct order.

}

#endif

static void change_wts_to_be_fracarea(Mesh *mesh, int num_entries,
                               int *iientries, double *factors) {


  // Get frac field
  MEField<> *elem_frac = mesh->GetField("elem_frac");
  if (!elem_frac) Throw() << "Trying to get elem_frac when it doesn't exist";

  // Process differently if mesh is split
  if (!mesh->is_split) {
    // Loop through weights dividing by dst_fraction
    for (int i=0; i<num_entries; i++) {
      int dst_id=iientries[2*i+1];

      //  Find the corresponding Mesh element
      Mesh::MeshObjIDMap::iterator mi =  mesh->map_find(MeshObj::ELEMENT, dst_id);
      if (mi == mesh->map_end(MeshObj::ELEMENT)) {
        Throw() << " destination id not found in destination mesh.";
      }

      // Get the element
      const MeshObj &elem = *mi;

      // Get frac data
      double *f=elem_frac->data(elem);
      double frac=*f;

      // If not 0.0 divide
      if (frac != 0.0) {
        factors[i] = factors[i]/frac;
      }
    }
  } else {
    // get map of elem ids to fraction
    std::map<int,double> id_to_frac;

    // Iterate through elements constucting id_to_frac list
    Mesh::iterator ei = mesh->elem_begin(), ee = mesh->elem_end();
    for (; ei != ee; ++ei) {
      MeshObj &elem = *ei;

      // Don't do non-local elements
      if (!GetAttr(elem).is_locally_owned()) continue;

      // Get the element id
      int eid=elem.get_id();

      // Get frac data
      double *f=elem_frac->data(elem);
      double frac=*f;

      // See if the element is part of a larger polygon
      std::map<UInt,double>::iterator mi =  mesh->split_id_to_frac.find(eid);

      // Not part of something larger, so just stick in map
      if (mi == mesh->split_id_to_frac.end()) {
        id_to_frac[eid] = frac;
        continue;
      }

      // It is part of original poly, so modify by fraction
      frac *= mi->second;

      // Translate id if necessary
      int orig_id;
      std::map<UInt,UInt>::iterator soi =  mesh->split_to_orig_id.find(eid);
      if (soi == mesh->split_to_orig_id.end()) {
        orig_id=eid;
      } else {
        orig_id=soi->second;
      }

      // either put this in or add it depending if id is in map
      std::map<int,double>::iterator ifi =  id_to_frac.find(orig_id);
      if (ifi == id_to_frac.end()) {
        id_to_frac[orig_id]=frac;
      } else {
        ifi->second += frac;
      }
    }

    // Loop through weights dividing by dst_fraction
    for (int i=0; i<num_entries; i++) {
      int dst_id=iientries[2*i+1];

      //  Find the corresponding fraction
      std::map<int,double>::iterator ifi =  id_to_frac.find(dst_id);
      if (ifi == id_to_frac.end()) {
        Throw() << " destination id not found in id_to_frac map.";
      }

      // Get fraction
      double frac=ifi->second;

      // If not 0.0 divide
      if (frac != 0.0) {
        factors[i] = factors[i]/frac;
      }
    }
  }
}

  // Only works for scalar data right now, but would be pretty easy to add more dimensions
void CpMeshDataToArray(Grid &grid, int staggerLoc, ESMCI::Mesh &mesh, ESMCI::Array &array, MEField<> *dataToArray) {
#undef  ESMC_METHOD
#define ESMC_METHOD "CpMeshDataToArray()"
  Trace __trace("CpMeshDataToArray()");

 int localrc;
 int rc;

  // Initialize the parallel environment for mesh (if not already done)
  ESMCI::Par::Init("MESHLOG", false /* use log */,VM::getCurrent(&localrc)->getMpi_c());
 if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL))
   throw localrc;  // bail out with exception

 bool is_sphere = grid.isSphere();

 // Loop nodes of the grid.  Here we loop all nodes, both owned and not.
   ESMCI::GridIter *gni=new ESMCI::GridIter(&grid,staggerLoc,true);

   // loop through all nodes in the Grid
   for(gni->toBeg(); !gni->isDone(); gni->adv()) {
     if(!gni->isLocal()) continue;

       // get the global id of this Grid node
       int gid=gni->getGlobalID();

       //  Find the corresponding Mesh node
       Mesh::MeshObjIDMap::iterator mi =  mesh.map_find(MeshObj::NODE, gid);
       if (mi == mesh.map_end(MeshObj::NODE)) {
         Throw() << "Grid entry not in mesh";
       }

       // Get the node
        const MeshObj &node = *mi;

       // Get the data
        double *data = dataToArray->data(node);

       // Put it into the Array
      gni->setArrayData(&array, *data);
   }


   // delete Grid Iters
   delete gni;

}
#undef  ESMC_METHOD


  // Assumes array is on center staggerloc of grid
  void CpMeshElemDataToArray(Grid &grid, int staggerloc, ESMCI::Mesh &mesh, ESMCI::Array &array, MEField<> *dataToArray) {
#undef  ESMC_METHOD
#define ESMC_METHOD "CpMeshElemDataToArray()"
  Trace __trace("CpMeshElemDataToArray()");

 int localrc;
 int rc;

 // If multi-tile jump to other way of doing things
 if (grid.getTileCount() > 1) {
   CpMeshElemDataToArrayCell(&grid, &mesh, &array, dataToArray);
   return;
 }

  // Initialize the parallel environment for mesh (if not already done)
  ESMCI::Par::Init("MESHLOG", false /* use log */,VM::getCurrent(&localrc)->getMpi_c());
 if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL))
   throw localrc;  // bail out with exception


    // Loop elemets of the grid.  Here we loop all elements, both owned and not.
    ESMCI::GridCellIter *gci=new ESMCI::GridCellIter(&grid,staggerloc);

    // loop through all nodes in the Grid
    for(gci->toBeg(); !gci->isDone(); gci->adv()) {

      // get the global id of this Grid node
      int gid=gci->getGlobalID();

      //  Find the corresponding Mesh element
      Mesh::MeshObjIDMap::iterator mi =  mesh.map_find(MeshObj::ELEMENT, gid);
      if (mi == mesh.map_end(MeshObj::ELEMENT)) {
        Throw() << "Grid entry not in mesh";
      }

      // Get the element
      const MeshObj &elem = *mi;

      // Only put it in if it's locally owned
      if (!GetAttr(elem).is_locally_owned()) continue;


       // Get the data
        double *data = dataToArray->data(elem);

        // DEBUG:  printf("G2M %d %f \n",gid,*data);

       // Put it into the Array
      gci->setArrayData(&array, *data);
   }

   // delete Grid Iters
   delete gci;
}


  void PutElemAreaIntoArray(Grid &grid, int staggerLoc, ESMCI::Mesh &mesh, ESMCI::Array &array) {
#undef  ESMC_METHOD
#define ESMC_METHOD "PutElemAreaIntoArray()"
    Trace __trace("PutElemAreaIntoArray()");

    int localrc;
    int rc;

#define  MAX_NUM_POLY_COORDS  60
#define  MAX_NUM_POLY_NODES_2D  30  // MAX_NUM_POLY_COORDS/2
#define  MAX_NUM_POLY_NODES_3D  20  // MAX_NUM_POLY_COORDS/3

    int num_poly_nodes;
    double poly_coords[MAX_NUM_POLY_COORDS];
    double tmp_coords[MAX_NUM_POLY_COORDS];


    // Initialize the parallel environment for mesh (if not already done)
    ESMCI::Par::Init("MESHLOG", false /* use log */,VM::getCurrent(&localrc)->getMpi_c());
    if (ESMC_LogDefault.MsgFoundError(localrc,ESMCI_ERR_PASSTHRU,ESMC_CONTEXT,NULL))
      throw localrc;  // bail out with exception


    // If multi-tile jump to other way of doing things
    if (grid.getTileCount() > 1) {
      PutElemAreaIntoArrayCell(&grid, &mesh, &array);
      return;
    }


    // Setup interator to Loop elemets of the grid.  Here we loop all elements, both owned and not.
    ESMCI::GridCellIter *gci=new ESMCI::GridCellIter(&grid,staggerLoc);


    // If an area field exists use that instead
    // TODO: replace this with something that doesn't require building a mesh first
    MEField<> *area_field = mesh.GetField("elem_area");
    if (area_field) {

      // loop through all nodes in the Grid
      for(gci->toBeg(); !gci->isDone(); gci->adv()) {

        // get the global id of this Grid node
        int gid=gci->getGlobalID();

        //  Find the corresponding Mesh element
        Mesh::MeshObjIDMap::iterator mi =  mesh.map_find(MeshObj::ELEMENT, gid);
        if (mi == mesh.map_end(MeshObj::ELEMENT)) {
          Throw() << "Grid entry not in mesh";
        }

        // Get the element
        const MeshObj &elem = *mi;

        // Only put it in if it's locally owned
        if (!GetAttr(elem).is_locally_owned()) continue;

        // Get area from field
        double *area=area_field->data(elem);

        // Put it into the Array
        gci->setArrayData(&array, *area);
      }

      return;
    }


    ////// Otherwise calculate areas.....

    // Get coord field
    MEField<> *cfield = mesh.GetCoordField();

    // Get dimensions
    int sdim=mesh.spatial_dim();
    int pdim=mesh.parametric_dim();

    // loop through all nodes in the Grid
    for(gci->toBeg(); !gci->isDone(); gci->adv()) {

      // get the global id of this Grid node
      int gid=gci->getGlobalID();

      //  Find the corresponding Mesh element
      Mesh::MeshObjIDMap::iterator mi =  mesh.map_find(MeshObj::ELEMENT, gid);
      if (mi == mesh.map_end(MeshObj::ELEMENT)) {
        Throw() << "Grid entry not in mesh";
      }

      // Get the element
      const MeshObj &elem = *mi;

      // Only put it in if it's locally owned
      if (!GetAttr(elem).is_locally_owned()) continue;

      // Get area depending on dimensions
      double area;

      if (pdim==2) {
        if (sdim==2) {
          get_elem_coords_2D_ccw(&elem, cfield, MAX_NUM_POLY_NODES_2D, tmp_coords, &num_poly_nodes, poly_coords);
          remove_0len_edges2D(&num_poly_nodes, poly_coords);
          area=area_of_flat_2D_polygon(num_poly_nodes, poly_coords);
        } else if (sdim==3) {
          get_elem_coords_3D_ccw(&elem, cfield, MAX_NUM_POLY_NODES_3D, tmp_coords, &num_poly_nodes, poly_coords);
          remove_0len_edges3D(&num_poly_nodes, poly_coords);
          area=great_circle_area(num_poly_nodes, poly_coords);
        }
      } else if (pdim==3) {
        if (sdim==3) {
          Phedra tmp_phedra=create_phedra_from_elem(&elem, cfield);
          area=tmp_phedra.calc_volume();
        } else {
          Throw() << "Meshes with parametric dimension == 3, but spatial dim != 3 not supported for computing areas";
        }
      } else {
        Throw() << "Meshes with parametric dimension != 2 or 3 not supported for computing areas";
      }


       // Put it into the Array
      gci->setArrayData(&array, area);
   }

   // delete Grid Iters
   delete gci;

}


// mesh set fraction
void ESMCI_meshsetfraction(Mesh **meshpp, double * fraction,
                   int*rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_meshsetfraction()"
  Trace __trace(" FTN(meshsetfraction) ");

  Mesh &mesh = **meshpp;

  try {
    MeshSetFraction(mesh, *fraction);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT, rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }
  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}

// Assumes array is center stagger loc
void ESMCI_xgrid_getfrac(Grid **gridpp,
                   Mesh **meshpp, ESMCI::Array **arraypp, int *staggerLoc,
                   int *rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_regrid_getfrac()"
  Trace __trace(" FTN_X(regrid_getfrac)()");

  ESMCI::Array &array = **arraypp;
  Mesh &mesh = **meshpp;
  Grid &grid = **gridpp;

  try {


    // Get the integration weights
    MEField<> *frac = mesh.GetField("elem_frac");
    if (!frac) Throw() << "Could not find elem_frac field on this mesh"
                             <<std::endl;

    CpMeshElemDataToArray(grid, *staggerLoc, mesh, array, frac);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT, rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}

////////////////////////////////////////////////////////////////////////////
// Assumes array is center stagger loc
void ESMCI_xgrid_getfrac2(Grid **gridpp,
                   Mesh **meshpp, ESMCI::Array **arraypp, int *staggerLoc,
                   int *rc) {
#undef  ESMC_METHOD
#define ESMC_METHOD "c_esmc_regrid_getfrac2()"
  Trace __trace(" FTN_X(regrid_getfrac2)()");

  ESMCI::Array &array = **arraypp;
  Mesh &mesh = **meshpp;
  Grid &grid = **gridpp;

  try {


    // Get the integration weights
    MEField<> *frac = mesh.GetField("elem_frac2");
    if (!frac) Throw() << "Could not find elem_frac2 field on this mesh"
                             <<std::endl;

    CpMeshElemDataToArray(grid, *staggerLoc, mesh, array, frac);

  } catch(std::exception &x) {
    // catch Mesh exception return code
    if (x.what()) {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          x.what(), ESMC_CONTEXT, rc);
    } else {
      ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
                                          "UNKNOWN", ESMC_CONTEXT, rc);
    }

    return;
  } catch(int localrc){
    // catch standard ESMF return code
    ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU, ESMC_CONTEXT, rc);
    return;
  } catch(...){
    ESMC_LogDefault.MsgFoundError(ESMC_RC_INTNRL_BAD,
      "- Caught unknown exception", ESMC_CONTEXT, rc);
    return;
  }

  // Set return code
  if (rc!=NULL) *rc = ESMF_SUCCESS;

}


void copy_rs_from_WMat_to_Array(WMat *wmat, ESMCI::Array *array) {

  // Look at ESMCI_Grid.C getGlobalID() for how to get sequence ids

  // Get Distgrid
  DistGrid *distgrid=array->getDistGrid();

  // Get reduced dimCount
  int redDimCount=array->getRank()-array->getTensorCount();

  // Only support up to a certian dim in this subroutine
#define CWTOA_MAXDIM 4
  if (redDimCount > CWTOA_MAXDIM) {
    int localrc;
    if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_RANK,
        " Regridding only supports info Fields (e.g. dstStatusField) of dimensions 4 and below ",
                                     ESMC_CONTEXT, &localrc)) throw localrc;
  }

  // Get computational bound arrays
  const int *clb_array=array->getComputationalLBound();
  const int *cub_array=array->getComputationalUBound();

  // Get exclusive bound arrays
  const int *elb_array=array->getExclusiveLBound();

  // Get number of localDEs
  int localDECount=distgrid->getDELayout()->getLocalDeCount();

  // Loop over localDes
  for (int lDE=0; lDE < localDECount; lDE++) {
    // Only support up to 4D for regridding right now
    int lbnd[CWTOA_MAXDIM]={0,0,0,0};
    int ubnd[CWTOA_MAXDIM]={0,0,0,0};
    int elbnd[CWTOA_MAXDIM]={0,0,0,0};
    int ind[CWTOA_MAXDIM]={0,0,0,0};
    int ind_m_elbnd[CWTOA_MAXDIM]={0,0,0,0};


    // Get localArray corresponding to this localDE
    LocalArray *localArray=array->getLocalarrayList()[lDE];

    // Get computational bounds of this DE
    const int *clb=clb_array+lDE*redDimCount;
    const int *cub=cub_array+lDE*redDimCount;

    // Get exclusive lower bounds of this DE
    const int *elb=elb_array+lDE*redDimCount;

    // Copy DE Bounds
    // NOTE: anything beyond array dimensions will be 0 & 0 so those loops won't iterate
    for (int i=0; i<redDimCount; i++) {
      lbnd[i]=clb[i];
      ubnd[i]=cub[i];
      elbnd[i]=elb[i];
    }


#if 0
    // dump whole matrix for debugging
    int j=0;
    WMat::WeightMap::iterator wi = wmat->begin_row(), we = wmat->end_row();
    for (; wi != we; ++wi) {
        const WMat::Entry &w = wi->first;
        std::vector<WMat::Entry> &wcol = wi->second;

        printf("%d col_size=%d\n",j,wcol.size());

        const WMat::Entry &wc = wcol[0];

        printf("%d dst_id=%d rs=%d\n",j,w.id,wc.id);

        j++;
      }
#endif

    // Loop indices in DE
    int pos=0;
    for (int i3=lbnd[3]; i3<=ubnd[3]; i3++) {
      ind[3]=i3;
      ind_m_elbnd[3]=i3-elbnd[3];
    for (int i2=lbnd[2]; i2<=ubnd[2]; i2++) {
      ind[2]=i2;
      ind_m_elbnd[2]=i2-elbnd[2];
    for (int i1=lbnd[1]; i1<=ubnd[1]; i1++) {
      ind[1]=i1;
      ind_m_elbnd[1]=i1-elbnd[1];
     for (int i0=lbnd[0]; i0<=ubnd[0]; i0++) {
      ind[0]=i0;
      ind_m_elbnd[0]=i0-elbnd[0];

      // Get sequence index of this point
      int localrc;
      int seq_ind;
      std::vector<int> seq_indV;
      localrc=distgrid->getSequenceIndexLocalDe(lDE,ind_m_elbnd,seq_indV);
      if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
        ESMC_CONTEXT, NULL)) throw localrc;  // bail out with exception
 /* XMRKX */

      if (seq_indV.size() > 0)
        seq_ind = seq_indV[0];
      else
        seq_ind = -1; // invalidate

      // If it's not in the WMat, then it's been masked out, so init. to masked
      ESMC_I4 regrid_status=ESMC_REGRID_STATUS_DST_MASKED;

      // Get regrid_status from WMat
      WMat::WeightMap::iterator wi = wmat->lower_bound_id_row(seq_ind);

      // If it's not found in the matrix, then just leave at init value
      if (wi != wmat->weights.end()) {

        // Get information about this entry in the matrix
        const WMat::Entry &w = wi->first;
        std::vector<WMat::Entry> &wcol = wi->second;

        // Make sure this entry has the correct id
        // (If it's not found in the matrix, then just leave at init value)
        if (w.id == seq_ind) {

          // Make sure there is only one answer
          if (wcol.size() != 1) {
            int localrc;
            if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_BAD,
                                             " more than one entry found for sequence id",
                                             ESMC_CONTEXT, &localrc)) throw localrc;
          }

          // Get the one column entry
          const WMat::Entry &wc = wcol[0];

          // Get the colum id
          regrid_status=wc.id;
        }
      }

      // printf("%d# %d si=%d rs=%d\n",Par::Rank(),pos,seq_ind,regrid_status);
      pos++;

      // Set regrid_status in local Array
      localArray->setData(ind, regrid_status);
    }
    }
    }
    }
  }
}


void copy_cnsv_rs_from_WMat_to_Array(WMat *wmat, ESMCI::Array *array) {
  // A small tolerence to take care of rounding effects
#define ZERO_TOL 1.0E-14

  // Look at ESMCI_Grid.C getGlobalID() for how to get sequence ids

  // Get Distgrid
  DistGrid *distgrid=array->getDistGrid();

  // Get reduced dimCount
  int redDimCount=array->getRank()-array->getTensorCount();

  // Only support up to a certian dim in this subroutine
#define CWTOA_MAXDIM 4
  if (redDimCount > CWTOA_MAXDIM) {
    int localrc;
    if(ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_RANK,
        " Regridding only supports info Fields (e.g. dstStatusField) of dimensions 4 and below ",
                                     ESMC_CONTEXT, &localrc)) throw localrc;
  }

  // Get computational bound arrays
  const int *clb_array=array->getComputationalLBound();
  const int *cub_array=array->getComputationalUBound();

  // Get exclusive bound arrays
  const int *elb_array=array->getExclusiveLBound();

  // Get number of localDEs
  int localDECount=distgrid->getDELayout()->getLocalDeCount();

  // Loop over localDes
  for (int lDE=0; lDE < localDECount; lDE++) {
    // Only support up to 4D for regridding right now
    int lbnd[CWTOA_MAXDIM]={0,0,0,0};
    int ubnd[CWTOA_MAXDIM]={0,0,0,0};
    int elbnd[CWTOA_MAXDIM]={0,0,0,0};
    int ind[CWTOA_MAXDIM]={0,0,0,0};
    int ind_m_elbnd[CWTOA_MAXDIM]={0,0,0,0};


    // Get localArray corresponding to this localDE
    LocalArray *localArray=array->getLocalarrayList()[lDE];

    // Get computational bounds of this DE
    const int *clb=clb_array+lDE*redDimCount;
    const int *cub=cub_array+lDE*redDimCount;

    // Get exclusive lower bounds of this DE
    const int *elb=elb_array+lDE*redDimCount;

    // Copy DE Bounds
    // NOTE: anything beyond array dimensions will be 0 & 0 so those loops won't iterate
    for (int i=0; i<redDimCount; i++) {
      lbnd[i]=clb[i];
      ubnd[i]=cub[i];
      elbnd[i]=elb[i];
    }


#if 0
    // dump whole matrix for debugging
    int j=0;
    WMat::WeightMap::iterator wi = wmat->begin_row(), we = wmat->end_row();
    for (; wi != we; ++wi) {
        const WMat::Entry &w = wi->first;
        std::vector<WMat::Entry> &wcol = wi->second;

        printf("%d col_size=%d\n",j,wcol.size());

        const WMat::Entry &wc = wcol[0];

        printf("%d dst_id=%d rs=%d\n",j,w.id,wc.id);

        j++;
      }
#endif

    // Loop indices in DE
    int pos=0;
    for (int i3=lbnd[3]; i3<=ubnd[3]; i3++) {
      ind[3]=i3;
      ind_m_elbnd[3]=i3-elbnd[3];
    for (int i2=lbnd[2]; i2<=ubnd[2]; i2++) {
      ind[2]=i2;
      ind_m_elbnd[2]=i2-elbnd[2];
    for (int i1=lbnd[1]; i1<=ubnd[1]; i1++) {
      ind[1]=i1;
      ind_m_elbnd[1]=i1-elbnd[1];
     for (int i0=lbnd[0]; i0<=ubnd[0]; i0++) {
      ind[0]=i0;
      ind_m_elbnd[0]=i0-elbnd[0];

      // Get sequence index of this point
      int localrc;
      int seq_ind;
      std::vector<int> seq_indV;
      localrc=distgrid->getSequenceIndexLocalDe(lDE,ind_m_elbnd,seq_indV);
      if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
        ESMC_CONTEXT, NULL)) throw localrc;  // bail out with exception

      if (seq_indV.size() > 0)
        seq_ind = seq_indV[0];
      else
        seq_ind = -1; // invalidate

      // If it's not in the WMat, then it's been masked out, so init. to masked
      ESMC_I4 regrid_status=ESMC_REGRID_STATUS_OUTSIDE;

      // Get regrid_status from WMat
      WMat::WeightMap::iterator wi = wmat->lower_bound_id_row(seq_ind);

      // If it's not found in the matrix, then just leave at init value
      if (wi != wmat->weights.end()) {

        // Get information about this entry in the matrix
        const WMat::Entry &w = wi->first;
        std::vector<WMat::Entry> &wcol = wi->second;

        // Make sure this entry has the correct id
        // (If it's not found in the matrix, then just leave at init value)
        if (w.id == seq_ind) {

          // If there are no entries then it's unmapped, so leave at unmapped and continue
          if (wcol.size() < 1) continue;

          // If this isn't a masked dst, then process
          if (wcol[0].idx != ESMC_REGRID_STATUS_DST_MASKED) {

            // Loop through processing
            regrid_status=0; // set to 0, so we can put in different statuses
            double tot_frac_used=0.0;
            for (UInt j = 0; j < wcol.size(); ++j) {
              const WMat::Entry &wc = wcol[j];

              //printf("dst_id=%d  type=%d src_id=%d frac=%g\n",w.id,wc.idx,wc.id,wc.value);

              // Set the flag for this type
              regrid_status |= (ESMC_I4)wc.idx;

              // Add in the fraction of the cell that's this type
              tot_frac_used += wc.value;
            }

            // If there's any left in the cell, then include an unmapped portion
            if ((1.0-tot_frac_used) > ZERO_TOL) regrid_status |= ESMC_REGRID_STATUS_OUTSIDE;
          } else {
            regrid_status=ESMC_REGRID_STATUS_DST_MASKED;
          }
        }
      }

      // printf("%d# %d si=%d rs=%d\n",Par::Rank(),pos,seq_ind,regrid_status);
      pos++;

      // Set regrid_status in local Array
      localArray->setData(ind, regrid_status);
    }
    }
    }
    }

  }
#undef ZERO_TOL
#undef cWtoA_MAXDIM
}

#undef  ESMC_METHOD
