[MOAB-dev] r2234 - MOAB/trunk/tools/iMesh

kraftche at mcs.anl.gov kraftche at mcs.anl.gov
Wed Nov 12 21:50:17 CST 2008


Author: kraftche
Date: 2008-11-12 21:50:17 -0600 (Wed, 12 Nov 2008)
New Revision: 2234

Added:
   MOAB/trunk/tools/iMesh/iMeshP_unit_tests.cpp
Modified:
   MOAB/trunk/tools/iMesh/Makefile.am
Log:
Adding an (incomplete) collection of unit tests for iMeshP.h.  All implemented
tests pass in serial.  More than half pass in parallel.  


Modified: MOAB/trunk/tools/iMesh/Makefile.am
===================================================================
--- MOAB/trunk/tools/iMesh/Makefile.am	2008-11-13 03:48:56 UTC (rev 2233)
+++ MOAB/trunk/tools/iMesh/Makefile.am	2008-11-13 03:50:17 UTC (rev 2234)
@@ -14,10 +14,7 @@
 
 # stuff for c test program
 check_PROGRAMS = testc_cbind
-testc_cbind_SOURCES = testc_cbind.c
 
-LDADD = libiMesh.la $(top_builddir)/libMOAB.la ${MOAB_CXX_LINKFLAGS} ${MOAB_CXX_LIBS}
-testc_cbind_DEPENDENCIES = libiMesh.la $(top_builddir)/libMOAB.la
 
 libiMesh_la_SOURCES = \
 	iMesh_MOAB.cpp \
@@ -36,15 +33,23 @@
   libiMesh_la_include_HEADERS += iMeshP.h iMeshP_f.h iMeshP_protos.h
   INCLUDES += -I$(top_srcdir)/parallel
 
-  check_PROGRAMS += partest
-  partest_SOURCES = partest.cpp
-  partest_DEPENDENCIES = libiMesh.la $(top_builddir)/libMOAB.la 
+  check_PROGRAMS += partest iMeshP_unit_tests
 
 #  check_PROGRAMS += ftest
 #  ftest_SOURCES = ftest.F
 #  ftest_DEPENDENCIES = libiMesh.la $(top_builddir)/libMOAB.la 
 endif
 
+LDADD = libiMesh.la $(top_builddir)/libMOAB.la ${MOAB_CXX_LINKFLAGS} ${MOAB_CXX_LIBS}
+TESTDEPS = libiMesh.la $(top_builddir)/libMOAB.la
+
+testc_cbind_SOURCES = testc_cbind.c
+testc_cbind_DEPENDENCIES = $(TESTDEPS)
+partest_SOURCES = partest.cpp
+partest_DEPENDENCIES = $(TESTDEPS)
+iMeshP_unit_tests_SOURCES = iMeshP_unit_tests.cpp
+iMeshP_unit_tests_DEPENDENCIES = $(TESTDEPS)
+
 lib_LTLIBRARIES = libiMesh.la
 
 libiMesh_la_includedir = $(includedir)
@@ -65,7 +70,7 @@
 # 'make prefix=/foo install', we don't know the correct install
 # directory until we're doing the install.
 install-data-hook:
-	echo "IMESH_DIR=${DESTDIR}${cfgdir}/.." >> $(DESTDIR)$(cfgdir)/iMesh-SIDL-Defs.inc
+	echo "IMESH_DIR=${cfgdir}/.." >> $(DESTDIR)$(cfgdir)/iMesh-SIDL-Defs.inc
 	echo "IMESH_INCLUDEDIR=${includedir}" >> $(DESTDIR)$(cfgdir)/iMesh-Defs.inc
 	echo "IMESH_LIBDIR=${libdir}" >> $(DESTDIR)$(cfgdir)/iMesh-Defs.inc
 	echo "MOAB_LIBDIR=${libdir}" >> $(DESTDIR)$(cfgdir)/iMesh-Defs.inc

Added: MOAB/trunk/tools/iMesh/iMeshP_unit_tests.cpp
===================================================================
--- MOAB/trunk/tools/iMesh/iMeshP_unit_tests.cpp	                        (rev 0)
+++ MOAB/trunk/tools/iMesh/iMeshP_unit_tests.cpp	2008-11-13 03:50:17 UTC (rev 2234)
@@ -0,0 +1,2051 @@
+#include "iMeshP.h"
+#include <mpi.h>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+#include <sstream>
+#include <assert.h>
+#include <math.h>
+#include <map>
+
+#define STRINGIFY_(X) #X
+#define STRINGIFY(X) STRINGIFY_(X)
+const char* const FILENAME = "iMeshP_test_file";
+
+
+/**************************************************************************
+                              Error Checking
+ **************************************************************************/
+
+#define CHKERR do { \
+  if (ierr) { \
+    std::cerr << "Error code  " << ierr << " at " << __FILE__ << ":" << __LINE__ << std::endl;\
+    return ierr; \
+  } \
+} while (false) 
+
+#define PCHECK do { ierr = is_any_proc_error(ierr); CHKERR; } while(false)
+
+#define ASSERT(A) do { \
+  if (is_any_proc_error(!(A))) { \
+    int rank = 0; \
+    MPI_Comm_rank( MPI_COMM_WORLD, &rank ); \
+    if (!rank) std::cerr << "Failed assertion: " #A << std::endl \
+                         << "  at " __FILE__ ":" << __LINE__ << std::endl; \
+    return 1; \
+  } } while (false)
+              
+// Test if is_my_error is non-zero on any processor in MPI_COMM_WORLD
+int is_any_proc_error( int is_my_error )
+{
+  int result;
+  int err = MPI_Allreduce( &is_my_error, &result, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD );
+  return err || result;
+}
+
+/**************************************************************************
+                           Test  Declarations
+ **************************************************************************/
+
+class PartMap;
+
+/**\brief Consistency check for parallel load
+ *
+ * All other tests depend on this one.
+ */
+int test_load( iMesh_Instance, iMeshP_PartitionHandle prtn, PartMap& map, int comm_size );
+
+
+/**\brief Test partition query methods
+ *
+ * Test:
+ * - iMeshP_getPartitionComm
+ * - iMeshP_getNumPartitions
+ * - iMeshP_getPartitions
+ */
+int test_get_partitions( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test part quyery methods
+ *
+ * Test:
+ * - iMeshP_getNumGlobalParts
+ * - iMeshP_getNumLocalParts
+ * - iMeshP_getLocalParts
+ */
+int test_get_parts( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test query by entity type
+ *
+ * Test:
+ * - iMeshP_getNumOfTypeAll
+ * - iMeshP_getNumOfType
+ * - iMeshP_getEntities
+ * - 
+ */
+int test_get_by_type( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test query by entity topology
+ *
+ * Test:
+ * - iMeshP_getNumOfTopoAll
+ * - iMeshP_getNumOfTopo
+ * - iMeshP_getEntities
+ * - 
+ */
+int test_get_by_topo( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test mapping from part id to part handle
+ * 
+ * Test:
+ * - iMeshP_getPartIdFromPartHandle
+ * - iMeshP_getPartIdsFromPartHandlesArr
+ * - iMeshP_getPartHandleFromPartId
+ * - iMeshP_getPartHandlesFromPartsIdsArr
+ * - iMeshP_getRankOfPart
+ * - iMeshP_getRankOfPartArr
+ */
+int test_part_id_handle( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test get part rank
+ *
+ * Tests:
+ * - iMeshP_getRankOfPart
+ * - iMeshP_getRankOfPartArr
+ */
+int test_part_rank( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test querying of part neighbors
+ *
+ * Test:
+ * - iMeshP_getNumPartNbors
+ * - iMeshP_getNumPartNborsArr
+ * - iMeshP_getPartNbors
+ * - iMeshP_getPartNborsArr
+ */
+int test_get_neighbors( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test querying of part boundary entities
+ *
+ * Test:
+ * - iMeshP_getNumPartBdryEnts
+ * - iMeshP_getPartBdryEnts
+ */
+int test_get_part_boundary( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test querying of part boundary entities
+ *
+ * Test:
+ * - iMeshP_initPartBdryEntIter
+ * - iMeshP_initPartBdryEntArrIter
+ */
+int test_part_boundary_iter( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test adjacent entity query
+ *
+ * Test:
+ * - iMeshP_getAdjEntities
+ */
+int test_get_adjacencies( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test entity iterators
+ *
+ * Test:
+ * - iMeshP_initEntIter
+ * - iMeshP_initEntArrIter
+ */
+int test_entity_iterator( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test entity owner queries
+ *
+ * Test:
+ * - iMeshP_getEntOwnerPart
+ * - iMeshP_getEntOwnerPartArr
+ * - iMeshP_isEntOwner
+ * - iMeshP_isEntOwnerArr
+ */
+int test_entity_owner( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test entity status
+ *
+ * Test:
+ * - iMeshP_getEntStatus
+ * - iMeshP_getEntStatusArr
+ */
+int test_entity_status( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test information about entity copies for interface entities
+ *
+ * Test:
+ * - iMeshP_getNumCopies
+ * - iMeshP_getCopyParts
+ * - iMeshP_getCopies
+ * - iMeshP_getCopyOnPart
+ * - iMeshP_getOwnerCopy
+ */
+int test_entity_copies( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test creation of ghost entities
+ *
+ * Test:
+ * - iMeshP_addGhostOf
+ * - iMeshP_createGhostEnts
+ */
+int test_create_ghost_ents( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+/**\brief Test commuinication of tag data
+ *
+ * Test:
+ * - iMeshP_pushTags
+ * - iMeshP_pushTagsEnt
+ */
+int test_push_tag_data( iMesh_Instance, iMeshP_PartitionHandle prtn, const PartMap& );
+
+
+/**************************************************************************
+                              Helper Funcions
+ **************************************************************************/
+ 
+class PartMap
+{
+public:
+  int num_parts() const 
+    { return sortedPartList.size(); }
+
+  iMeshP_Part part_id_from_local_id( int local_id ) const
+    { return sortedPartList[idx_from_local_id(local_id)]; }
+    
+  int local_id_from_part_id( iMeshP_Part part ) const
+    { return partLocalIds[idx_from_part_id(part)]; }
+  
+  int rank_from_part_id( iMeshP_Part part ) const
+    { return partRanks[idx_from_part_id(part)]; }
+  
+  int rank_from_local_id( int id ) const
+    { return partRanks[idx_from_local_id(id)]; }
+  
+  int count_from_rank( int rank ) const
+    { return std::count( partRanks.begin(), partRanks.end(), rank ); }
+    
+  void part_id_from_rank( int rank, std::vector<iMeshP_Part>& parts ) const;
+  
+  void local_id_from_rank( int rank, std::vector<int>& ids ) const;
+  
+  const std::vector<iMeshP_Part>& get_parts() const 
+    { return sortedPartList; }
+  
+  const std::vector<int>& get_ranks() const 
+    { return partRanks; }
+
+  int build_map( iMesh_Instance imesh,
+                 iMeshP_PartitionHandle partition,
+                 int num_expected_parts );
+
+  static int part_from_coords( iMesh_Instance imesh, 
+                               iMeshP_PartHandle part, 
+                               int& id_out );
+
+private:
+  inline int idx_from_part_id( iMeshP_Part id ) const
+    { return std::lower_bound( sortedPartList.begin(), sortedPartList.end(), id ) 
+          -  sortedPartList.begin(); }
+  inline int idx_from_local_id( int id ) const
+    { return localIdReverseMap[id]; }
+    
+  std::vector<iMeshP_Part> sortedPartList;
+  std::vector<int> partRanks;
+  std::vector<int> partLocalIds;
+  std::vector<int> localIdReverseMap;
+};
+
+/**\brief Create mesh for use in parallel tests */
+int create_mesh( const char* filename, int num_parts );
+
+int get_local_parts( iMesh_Instance instance,
+                     iMeshP_PartitionHandle prtn,
+                     std::vector<iMeshP_PartHandle>& handles,
+                     std::vector<iMeshP_Part>* ids = 0 )
+{
+  iMeshP_PartHandle* arr = 0;
+  int ierr, alloc = 0, size;
+  iMeshP_getLocalParts( instance, prtn, &arr, &alloc, &size, &ierr );
+  CHKERR;
+  handles.resize( size );
+  std::copy( arr, arr + size, handles.begin() );
+  if (!ids)
+    return iBase_SUCCESS;
+  
+  ids->resize( size );
+  alloc = size;
+  iMeshP_Part* ptr = &(*ids)[0];
+  iMeshP_getPartIdsFromPartHandlesArr( instance, prtn, &handles[0], handles.size(),
+                                       &ptr, &alloc, &size, &ierr );
+  CHKERR;
+  assert( size == (int)ids->size() );
+  assert( ptr == &(*ids)[0] );
+  return iBase_SUCCESS;
+}
+
+/**************************************************************************
+                              Main Method
+ **************************************************************************/
+
+#define RUN_TEST(A) run_test( &A, #A, imesh, prtn, map )
+
+int run_test( int (*func)(iMesh_Instance, iMeshP_PartitionHandle, const PartMap&), 
+              const char* func_name,
+              iMesh_Instance data,
+              iMeshP_PartitionHandle prtn,
+              const PartMap& map )
+{
+  int result = (*func)(data,prtn,map);
+  int is_err = is_any_proc_error( result );
+  int rank;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  if (rank == 0) {
+    if (is_err) 
+      std::cout << func_name << " : FAILED!!" << std::endl;
+    else
+      std::cout << func_name << " : success" << std::endl;
+  }
+  
+  return is_err;
+}
+
+/**\brief Create mesh for use in parallel tests */
+int create_mesh( const char* filename, int num_parts );
+
+
+int main( int argc, char* argv[] )
+{
+  MPI_Init(&argc, &argv);
+  int size, rank, ierr = 1;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  MPI_Comm_size( MPI_COMM_WORLD, &size );
+
+  if (rank == 0) {
+    ierr = create_mesh( FILENAME, size );
+  }
+  MPI_Scatter( &ierr, 1, MPI_INT, &ierr, 1, MPI_INT, 0, MPI_COMM_WORLD );
+  if (ierr) {
+    if (rank == 0) {
+      std::cerr << "Failed to create input test file on root processor.  Aborting."
+                << std::endl;
+    }
+    abort();
+  }
+  
+  iMesh_Instance imesh;
+  iMesh_newMesh( 0, &imesh, &ierr, 0 );
+  PCHECK;
+  
+  iMeshP_PartitionHandle prtn;
+  iMeshP_createPartitionAll( imesh, MPI_COMM_WORLD, &prtn, &ierr );
+  PCHECK;
+  
+  PartMap map;
+  int num_errors =  test_load( imesh, prtn, map, size );
+  if (num_errors) {
+    if (rank == 0) {
+      std::cerr << "Failed to load input mesh." << std::endl
+                << "Cannot run further tests." << std::endl
+                << "ABORTING" << std::endl;
+    }
+    abort();
+  }
+
+  num_errors += RUN_TEST( test_get_partitions );
+  num_errors += RUN_TEST( test_get_parts );
+  num_errors += RUN_TEST( test_get_by_type );
+  num_errors += RUN_TEST( test_get_by_topo );
+  num_errors += RUN_TEST( test_part_id_handle );
+  num_errors += RUN_TEST( test_part_rank );
+  num_errors += RUN_TEST( test_get_neighbors );
+  num_errors += RUN_TEST( test_get_part_boundary );
+  num_errors += RUN_TEST( test_part_boundary_iter );
+  num_errors += RUN_TEST( test_get_adjacencies );
+  num_errors += RUN_TEST( test_entity_iterator );
+  num_errors += RUN_TEST( test_entity_owner );
+  num_errors += RUN_TEST( test_entity_status );
+  num_errors += RUN_TEST( test_entity_copies );
+  num_errors += RUN_TEST( test_create_ghost_ents );
+  num_errors += RUN_TEST( test_push_tag_data );
+  
+    // wait until all procs are done before writing summary data
+  std::cout.flush();
+  MPI_Barrier( MPI_COMM_WORLD );
+  
+  if (rank == 0) {
+    if (!num_errors) 
+      std::cout << "All tests passed" << std::endl;
+    else
+      std::cout << num_errors << " TESTS FAILED!" << std::endl;
+  }
+  
+  MPI_Finalize();
+  return num_errors;
+}
+
+// Create a mesh
+//
+// 
+// Groups of four quads will be arranged into parts as follows:
+// +------+------+------+------+------+-----
+// |             |             |
+// |             |             |
+// +    Part 0   +    Part 2   +    Part 4
+// |             |             |
+// |             |             |
+// +------+------+------+------+------+-----
+// |             |             |
+// |             |             |
+// +    Part 1   +    Part 3   +    Part 5
+// |             |             |
+// |             |             |
+// +------+------+------+------+------+-----
+//
+// Vertices will be enumerated as follows:
+// 1------6-----11-----16-----21-----26-----
+// |             |             |
+// |             |             |
+// 2      7     12     17     22     27
+// |             |             |
+// |             |             |
+// 3------8-----13-----18-----23-----28-----
+// |             |             |
+// |             |             |
+// 4      9     14     19     24     29
+// |             |             |
+// |             |             |
+// 5-----10-----15-----20-----25-----30-----
+//
+// Element IDs will be [4*rank+1,4*rank+5]
+template <int size> struct EHARR
+{ 
+  iBase_EntityHandle h[size];
+  iBase_EntityHandle& operator[](int i){ return h[i]; } 
+  operator iBase_EntityHandle*() { return h; }
+};
+int create_mesh( const char* filename, int num_parts )
+{
+  const char* tagname = "GLOBAL_ID";
+  int ierr;
+  
+  iMesh_Instance imesh;
+  iMesh_newMesh( 0, &imesh, &ierr, 0 ); CHKERR;
+  
+  const int num_full_cols = 2 * (num_parts / 2);
+  const int need_half_cols = num_parts % 2;
+  const int num_cols = num_full_cols + 2*need_half_cols;
+  const int num_vtx = 5+5*num_cols - 6*(num_parts%2);
+  std::vector< EHARR<5> > vertices( num_cols + 1 );
+  std::vector< EHARR<4> > elements( num_cols );
+  std::vector<int> vertex_ids( num_vtx );
+  std::vector<iBase_EntityHandle> vertex_list(num_vtx);
+  for (int i = 0; i < num_vtx; ++i)
+    vertex_ids[i] = i+1;
+  
+    // create vertices
+  int vl_pos = 0;
+  for (int i = 0; i <= num_cols; ++i) {
+    double coords[15] = { i, 0, 0,
+                          i, 1, 0,
+                          i, 2, 0,
+                          i, 3, 0,
+                          i, 4, 0 };
+    iBase_EntityHandle* ptr = vertices[i];
+    const int n = (num_full_cols && i <= num_full_cols) ? 5 : 3;
+    int junk1 = n, junk2 = n;
+    iMesh_createVtxArr( imesh, n, iBase_INTERLEAVED, coords, 3*n,
+                        &ptr, &junk1, &junk2, &ierr ); CHKERR;
+    assert( ptr == vertices[i] );
+    assert( junk1 == n );
+    assert( junk2 == n );
+    for (int j = 0; j < n; ++j)
+      vertex_list[vl_pos++] = vertices[i][j];
+  }
+  
+    // create elements
+  for (int i = 0; i < num_cols; ++i) {
+    iBase_EntityHandle conn[16];
+    for (int j = 0; j < 4; ++j) {
+      conn[4*j  ] = vertices[i  ][j  ];
+      conn[4*j+1] = vertices[i  ][j+1];
+      conn[4*j+2] = vertices[i+1][j+1];
+      conn[4*j+3] = vertices[i+1][j  ];
+    }
+    iBase_EntityHandle* ptr = elements[i];
+    const int n = (i < num_full_cols) ? 4 : 2;
+    int junk1 = n, junk2 = n, junk3 = n, junk4 = n;
+    int stat[4];
+    int* ptr2 = stat;
+    iMesh_createEntArr( imesh, 
+                        iMesh_QUADRILATERAL, 
+                        conn, 4*n,
+                        &ptr, &junk1, &junk2,
+                        &ptr2, &junk3, &junk4, 
+                        &ierr ); CHKERR;
+    assert( ptr == elements[i] );
+    assert( junk1 == n );
+    assert( junk2 == n );
+    assert( ptr2 == stat );
+    assert( junk3 == n );
+    assert( junk4 == n );
+  }
+  
+    // create partition
+  iMeshP_PartitionHandle partition;
+  iMeshP_createPartitionAll( imesh, MPI_COMM_SELF, &partition, &ierr ); CHKERR;
+  for (int i = 0; i < num_parts; ++i) {
+    iMeshP_PartHandle part;
+    iMeshP_createPart( imesh, partition, &part, &ierr ); CHKERR;
+    iBase_EntityHandle quads[] = { elements[i/2  ][2*(i%2)  ],
+                                   elements[i/2+1][2*(i%2)  ],
+                                   elements[i/2  ][2*(i%2)+1],
+                                   elements[i/2+1][2*(i%2)+1] };
+    iBase_EntitySetHandle set = part;
+    iMesh_addEntArrToSet( imesh, quads, 4, &set, &ierr ); CHKERR;
+    assert(set == part);
+  }
+  
+    // assign global ids to vertices
+  iBase_TagHandle id_tag = 0;
+  iMesh_getTagHandle( imesh, tagname, &id_tag, &ierr, strlen(tagname) );
+  if (iBase_SUCCESS == ierr) {
+    int tag_size, tag_type;
+    iMesh_getTagSizeValues( imesh, id_tag, &tag_size, &ierr );
+    CHKERR;
+    if (tag_size != 1)
+      return iBase_TAG_ALREADY_EXISTS;
+    iMesh_getTagType( imesh, id_tag, &tag_type, &ierr );
+    CHKERR;
+    if (tag_type != iBase_INTEGER)
+      return iBase_TAG_ALREADY_EXISTS;
+  }
+  else {
+    iMesh_createTag( imesh, tagname, 1, iBase_INTEGER, &id_tag, &ierr, strlen(tagname) );
+    CHKERR;
+  }
+  iMesh_setIntArrData( imesh, &vertex_list[0], num_vtx, id_tag, &vertex_ids[0], num_vtx, &ierr );
+  CHKERR;
+  
+    // write file
+  iBase_EntitySetHandle root_set;
+  iMesh_getRootSet( imesh, &root_set, &ierr );
+  iMeshP_save( imesh, partition, root_set, filename, 0, &ierr, strlen(filename), 0 );
+  CHKERR;
+  
+  iMesh_dtor( imesh, &ierr ); CHKERR;
+  
+  return 0;
+}
+
+
+
+/**************************************************************************
+                           Test  Implementations
+ **************************************************************************/
+
+int test_load( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, PartMap& map, int proc_size )
+{
+  int ierr;
+  
+  iBase_EntitySetHandle root_set;
+  iMesh_getRootSet( imesh, &root_set, &ierr );
+  iMeshP_load( imesh, prtn, root_set, FILENAME, 0, &ierr, strlen(FILENAME), 0 );
+  PCHECK;
+
+  
+  ierr = map.build_map( imesh, prtn, proc_size );
+  CHKERR;
+  return iBase_SUCCESS;
+}
+
+
+/**\brief Test partition query methods
+ *
+ * Test:
+ * - iMeshP_getPartitionComm
+ * - iMeshP_getNumPartitions
+ * - iMeshP_getPartitions
+ */
+int test_get_partitions( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  int ierr;
+  
+    // test iMeshP_getPartitionCom
+  MPI_Comm comm = MPI_COMM_SELF;
+  iMeshP_getPartitionComm( imesh, prtn, &comm, &ierr );
+  PCHECK;
+  ASSERT(comm == MPI_COMM_WORLD);
+  
+  
+    // test iMeshP_getPartitions
+  iMeshP_PartitionHandle* array = 0;
+  int alloc = 0, size = -1;
+  iMeshP_getPartitions( imesh, &array, &alloc, &size, &ierr );
+  PCHECK;
+  ASSERT(array != 0);
+  ASSERT(alloc == size);
+  ASSERT(size > 0);
+  int idx = std::find(array, array+size, prtn) - array;
+  free(array);
+  ASSERT(idx < size);
+  
+    // test iMesP_getNumPartitions
+  int size2 = -1;
+  iMeshP_getNumPartitions( imesh, &size2, &ierr );
+  PCHECK;
+  ASSERT(size2 == size);
+  return 0;
+}
+
+  
+
+/**\brief Test part quyery methods
+ *
+ * Test:
+ * - iMeshP_getNumGlobalParts
+ * - iMeshP_getNumLocalParts
+ * - iMeshP_getLocalParts
+ */
+int test_get_parts( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int size, rank, ierr;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  MPI_Comm_size( MPI_COMM_WORLD, &size );
+  
+  int num_part_g;
+  iMeshP_getNumGlobalParts( imesh, prtn, &num_part_g, &ierr );
+  PCHECK;
+  ASSERT( num_part_g == map.num_parts() );
+  
+  int num_part_l;
+  iMeshP_getNumLocalParts( imesh, prtn, &num_part_l, &ierr );
+  PCHECK;
+  ASSERT( num_part_l == map.count_from_rank( rank ) );
+  
+  std::vector<iMeshP_PartHandle> parts(num_part_l);
+  iMeshP_PartHandle* ptr = &parts[0];
+  int junk1 = num_part_l, count = -1;
+  iMeshP_getLocalParts( imesh, prtn, &ptr, &junk1, &count, &ierr );
+  PCHECK;
+  assert( ptr == &parts[0] );
+  assert( junk1 == num_part_l );
+  ASSERT( count == num_part_l );
+  
+  return iBase_SUCCESS;
+}
+
+static int get_entities( iMesh_Instance imesh,
+                         iBase_EntitySetHandle set,
+                         iBase_EntityType type,
+                         iMesh_EntityTopology topo,
+                         std::vector<iBase_EntityHandle>& entities )
+{
+  iBase_EntitySetHandle* array = 0;
+  int junk = 0, size = 0, err;
+  iMesh_getEntities( imesh, set, type, topo, &array, &junk, &size, &err );
+  if (!err) {
+    entities.clear();
+    entities.resize( size );
+    std::copy( array, array + size, entities.begin() );
+    free( array );
+  }
+  return err;
+}
+
+static int get_intersection( iMesh_Instance imesh,
+                             iBase_EntitySetHandle set1,
+                             iBase_EntitySetHandle set2,
+                             iBase_EntityType type,
+                             iMesh_EntityTopology topo,
+                             std::vector<iBase_EntityHandle>& entities )
+{
+  std::vector<iBase_EntityHandle> l1, l2;
+  int err;
+  err = get_entities( imesh, set1, type, topo, l1 );
+  if (err)
+    return err;
+  err = get_entities( imesh, set2, type, topo, l2 );
+  if (err)
+    return err;
+  
+  std::sort( l1.begin(), l1.end() );
+  std::sort( l2.begin(), l2.end() );
+  std::set_intersection( l1.begin(), l1.end(),
+                         l2.begin(), l2.end(),
+                         std::back_inserter( entities ) );
+  return iBase_SUCCESS;
+}
+
+static int test_get_by_type_topo_all( iMesh_Instance imesh,
+                                      iMeshP_PartitionHandle prtn,
+                                      bool test_type,
+                                      int num_parts )
+{
+    // calculate number of quads and vertices in entire mesh
+    // from number of parts (see create_mesh(..) function.)
+  const int expected_global_quad_count = 4 * num_parts;
+  const int num_col = 2 * (num_parts / 2 + num_parts % 2);
+  const int expected_global_vtx_count = 5 + 5*num_col - 6*(num_parts % 2);
+  
+    // test getNumOf*All for root set
+  int ierr, count;
+  iBase_EntitySetHandle root;
+  iMesh_getRootSet( imesh, &root, &ierr );  
+  if (test_type) 
+    iMeshP_getNumOfTypeAll( imesh, prtn, root, iBase_VERTEX, &count, &ierr );
+  else
+    iMeshP_getNumOfTopoAll( imesh, prtn, root, iMesh_POINT, &count, &ierr );
+  PCHECK;
+  ASSERT( count == expected_global_vtx_count );
+  if (test_type) 
+    iMeshP_getNumOfTypeAll( imesh, prtn, root, iBase_FACE, &count, &ierr );
+  else
+    iMeshP_getNumOfTopoAll( imesh, prtn, root, iMesh_QUADRILATERAL, &count, &ierr );
+  PCHECK;
+  ASSERT( count == expected_global_quad_count );
+  
+    // create an entity set containing half of the quads
+  std::vector<iBase_EntityHandle> all_quads, half_quads;
+  ierr = get_entities( imesh, root, iBase_FACE, iMesh_QUADRILATERAL, all_quads );
+  assert( 0 == all_quads.size() % 2 );
+  half_quads.resize(all_quads.size()/2);
+  for (size_t i = 0; i < all_quads.size() / 2; ++i)
+    half_quads[i] = all_quads[2*i];
+  iBase_EntitySetHandle set;
+  iMesh_createEntSet( imesh, 1, &set, &ierr );
+  CHKERR;
+  iMesh_addEntArrToSet( imesh, &half_quads[0], half_quads.size(), &set, &ierr );
+  CHKERR;
+  
+    // test getNumOf*All with defined set
+  if (test_type) 
+    iMeshP_getNumOfTypeAll( imesh, prtn, set, iBase_VERTEX, &count, &ierr );
+  else
+    iMeshP_getNumOfTopoAll( imesh, prtn, set, iMesh_POINT, &count, &ierr );
+  PCHECK;
+  ASSERT( count == 0 );
+  if (test_type) 
+    iMeshP_getNumOfTypeAll( imesh, prtn, set, iBase_FACE, &count, &ierr );
+  else
+    iMeshP_getNumOfTopoAll( imesh, prtn, set, iMesh_QUADRILATERAL, &count, &ierr );
+  PCHECK;
+  ASSERT( count == expected_global_quad_count/2 );
+  
+  return 0;
+}
+
+static int test_get_by_type_topo_local( iMesh_Instance imesh,
+                                        iMeshP_PartitionHandle prtn,
+                                        bool test_type )
+{
+  int ierr;
+  iBase_EntitySetHandle root;
+  iMesh_getRootSet( imesh, &root, &ierr );  
+ 
+    // select a single part
+  std::vector<iMeshP_PartHandle> parts;
+  ierr = get_local_parts( imesh, prtn, parts );
+  CHKERR;
+  iMeshP_PartHandle part = parts.front();
+  
+    // get the entities contained in the part
+  std::vector<iBase_EntityHandle> part_quads, part_all;
+  ierr = get_entities( imesh, part, iBase_FACE, iMesh_QUADRILATERAL, part_quads ); CHKERR;
+  ierr = get_entities( imesh, part, iBase_ALL_TYPES, iMesh_ALL_TOPOLOGIES, part_all ); CHKERR;
+  
+    // compare local counts (using root set)
+    
+  int count;
+  if (test_type)
+    iMeshP_getNumOfType( imesh, prtn, part, root, iBase_FACE, &count, &ierr );
+  else
+    iMeshP_getNumOfTopo( imesh, prtn, part, root, iMesh_QUADRILATERAL, &count, &ierr );
+  CHKERR;
+  ASSERT( count == (int)part_quads.size() );
+
+  if (test_type)
+    iMeshP_getNumOfType( imesh, prtn, part, root, iBase_ALL_TYPES, &count, &ierr );
+  else
+    iMeshP_getNumOfTopo( imesh, prtn, part, root, iMesh_ALL_TOPOLOGIES, &count, &ierr );
+  CHKERR;
+  ASSERT( count == (int)part_all.size() );
+  
+    // compare local contents (using root set)
+
+  iBase_EntityHandle* ptr = 0;
+  int num_ent, junk1 = 0;
+  iMeshP_getEntities( imesh, prtn, part, root, test_type ? iBase_FACE : iBase_ALL_TYPES,
+                      test_type ? iMesh_ALL_TOPOLOGIES : iMesh_QUADRILATERAL,
+                      &ptr, &junk1, &num_ent, &ierr ); CHKERR;
+  std::vector<iBase_EntityHandle> act_quads( ptr, ptr+num_ent );
+  free(ptr);
+  junk1 = num_ent = 0;
+  iMeshP_getEntities( imesh, prtn, part, root, iBase_ALL_TYPES,
+                      iMesh_ALL_TOPOLOGIES,
+                      &ptr, &junk1, &num_ent, &ierr ); CHKERR;
+  std::vector<iBase_EntityHandle> act_all( ptr, ptr+num_ent );
+  free(ptr);
+  std::sort( part_quads.begin(), part_quads.end() );
+  std::sort( part_all.begin(), part_all.end() );
+  std::sort( act_quads.begin(), act_quads.end() );
+  std::sort( act_all.begin(), act_all.end() );
+  ASSERT( part_quads == act_quads );
+  ASSERT( part_all   == act_all   );
+  
+    // create an entity set containing half of the quads from the part
+  std::vector<iBase_EntityHandle> half_quads(part_quads.size()/2);
+  for (size_t i = 0; i < half_quads.size(); ++i)
+    half_quads[i] = part_quads[2*i];
+  iBase_EntitySetHandle set;
+  iMesh_createEntSet( imesh, 1, &set, &ierr );
+  CHKERR;
+  iMesh_addEntArrToSet( imesh, &half_quads[0], half_quads.size(), &set, &ierr );
+  CHKERR;
+  
+    // check if there exists any quads not in the part that we 
+    // can add to the set
+  std::vector<iBase_EntityHandle> all_quads, other_quads;
+  ierr = get_entities( imesh, root, iBase_FACE, iMesh_QUADRILATERAL, all_quads); CHKERR;
+  std::sort( all_quads.begin(), all_quads.end() );
+  std::sort( part_quads.begin(), part_quads.end() );
+  std::set_difference( all_quads.begin(), all_quads.end(),
+                       part_quads.begin(), part_quads.end(),
+                       std::back_inserter( other_quads ) );
+  iMesh_addEntArrToSet( imesh, &other_quads[0], other_quads.size(), &set, &ierr );
+  CHKERR;
+  
+    // compare local counts (using non-root set)
+    
+  if (test_type)
+    iMeshP_getNumOfType( imesh, prtn, part, set, iBase_FACE, &count, &ierr );
+  else
+    iMeshP_getNumOfTopo( imesh, prtn, part, set, iMesh_QUADRILATERAL, &count, &ierr );
+  CHKERR;
+  ASSERT( count == (int)half_quads.size() );
+
+  if (test_type)
+    iMeshP_getNumOfType( imesh, prtn, part, set, iBase_VERTEX, &count, &ierr );
+  else
+    iMeshP_getNumOfTopo( imesh, prtn, part, set, iMesh_POINT, &count, &ierr );
+  CHKERR;
+  ASSERT( count == 0 );
+  
+    // compare local contents (using non-root set)
+
+  junk1 = 0; num_ent = 0;
+  iMeshP_getEntities( imesh, prtn, part, set, test_type ? iBase_FACE : iBase_ALL_TYPES,
+                      test_type ? iMesh_ALL_TOPOLOGIES : iMesh_QUADRILATERAL,
+                      &ptr, &junk1, &num_ent, &ierr ); CHKERR;
+  act_quads.resize(num_ent);
+  std::copy( ptr, ptr + num_ent, act_quads.begin() );
+  free(ptr);
+  std::sort( half_quads.begin(), half_quads.end() );
+  std::sort( act_quads.begin(), act_quads.end() );
+  ASSERT( act_quads == half_quads );
+  
+  return iBase_SUCCESS;
+}
+    
+
+
+/**\brief Test query by entity type
+ *
+ * Test:
+ * - iMeshP_getNumOfTypeAll
+ * - iMeshP_getNumOfType
+ * - iMeshP_getEntities
+ * - 
+ */
+int test_get_by_type( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr;
+  ierr = test_get_by_type_topo_all( imesh, prtn, true, map.num_parts() );
+  PCHECK;
+  ierr = test_get_by_type_topo_local( imesh, prtn, true );
+  PCHECK;
+  return 0;
+}
+
+/**\brief Test query by entity topology
+ *
+ * Test:
+ * - iMeshP_getNumOfTopoAll
+ * - iMeshP_getNumOfTopo
+ * - iMeshP_getEntities
+ * - 
+ */
+int test_get_by_topo( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr;
+  ierr = test_get_by_type_topo_all( imesh, prtn, false, map.num_parts() );
+  PCHECK;
+  ierr = test_get_by_type_topo_local( imesh, prtn, false );
+  PCHECK;
+  return 0;
+}
+
+
+/**\brief Test mapping from part id to part handle
+ * 
+ * Test:
+ * - iMeshP_getPartIdFromPartHandle
+ * - iMeshP_getPartIdsFromPartHandlesArr
+ * - iMeshP_getPartHandleFromPartId
+ * - iMeshP_getPartHandlesFromPartsIdsArr
+ */
+int test_part_id_handle( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+    // get local part ids
+  int rank, ierr;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  std::vector<iMeshP_Part> ids;
+  map.part_id_from_rank( rank, ids );
+  
+    // check single-part functions and build list of part handles
+  std::vector<iMeshP_PartHandle> handles( ids.size() );
+  size_t i;
+  for (i = 0; i < ids.size(); ++i) {
+    iMeshP_getPartHandleFromPartId( imesh, prtn, ids[i], &handles[i], &ierr );
+    CHKERR;
+    iMeshP_Part id;
+    iMeshP_getPartIdFromPartHandle( imesh, prtn, handles[i], &id, &ierr );
+    CHKERR;
+    if (id != ids[i])
+      break;
+  }
+  ASSERT( i == ids.size() );
+  
+    // test iMeshP_getPartIdsFromPartHandlesArr
+  std::vector<iMeshP_Part> ids2( ids.size() );
+  int junk1 = ids.size(), junk2 = 0;
+  iMeshP_Part* ptr = &ids2[0];
+  iMeshP_getPartIdsFromPartHandlesArr( imesh, prtn, &handles[0], handles.size(),
+                                       &ptr, &junk1, &junk2, &ierr );
+  PCHECK;
+  ASSERT( ptr == &ids2[0] );
+  ASSERT( junk2 == (int)ids2.size() );
+  ASSERT( ids == ids2 );
+  
+    // test iMeshP_getPartHandlesFromPartsIdsArr
+  std::vector<iMeshP_PartHandle> handles2(handles.size());
+  junk1 = handles.size();
+  junk2 = 0;
+  iMeshP_PartHandle* ptr2 = &handles2[0];
+  iMeshP_getPartHandlesFromPartsIdsArr( imesh, prtn, &ids[0], ids.size(),
+                                        &ptr2, &junk1, &junk2, &ierr );
+  PCHECK;
+  ASSERT( ptr2 == &handles2[0] );
+  ASSERT( junk2 == (int)handles2.size() );
+  ASSERT( handles == handles2 );
+  
+  return 0;
+}
+
+/**\brief Test get part rank
+ *
+ * Tests:
+ * - iMeshP_getRankOfPart
+ * - iMeshP_getRankOfPartArr
+ */
+int test_part_rank( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr = 0, rank;
+  std::vector<iMeshP_Part> invalid, failed;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  
+    // test iMeshP_getRankOfPart
+  for (size_t i = 0; i < map.get_parts().size(); ++i) {
+    int pr;
+    iMeshP_getRankOfPart( imesh, prtn, map.get_parts()[i], &pr, &ierr );
+    if (iBase_SUCCESS != ierr)
+      failed.push_back( map.get_parts()[i] );
+    else if (pr != map.get_ranks()[i])
+      invalid.push_back( map.get_parts()[i] );
+  }
+  if (!failed.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getRankOfPart failed for " << failed.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getRankOfPart was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  PCHECK;
+   
+    // test iMeshP_getRankOfPartArr
+  std::vector<int> ranks( map.get_parts().size() );
+  int junk1 = ranks.size(), junk2, *ptr = &ranks[0];
+  iMeshP_getRankOfPartArr( imesh, prtn, &map.get_parts()[0], map.get_parts().size(),
+                           &ptr, &junk1, &junk2, &ierr );
+  PCHECK; 
+  assert( ptr == &ranks[0] );
+  assert( junk1 == (int)ranks.size() );
+  ASSERT( junk2 == (int)ranks.size() );
+  for (size_t i = 0; i < map.get_parts().size(); ++i) {
+    if (ranks[i] != map.get_ranks()[i])
+      invalid.push_back( map.get_parts()[i] );
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getRankOfPartArr was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  PCHECK;
+  
+  return 0;
+}
+   
+
+// see create_mesh(..)
+static void get_part_neighbors( int logical_part_id,
+                                int num_parts,
+                                int neighbors[5],
+                                int& num_neighbors )
+{
+  num_neighbors = 0;
+  if (logical_part_id + 1 < num_parts)
+    neighbors[num_neighbors++] = logical_part_id + 1;
+  if (logical_part_id + 2 < num_parts)
+    neighbors[num_neighbors++] = logical_part_id + 2;
+  if (logical_part_id % 2) {
+    neighbors[num_neighbors++] = logical_part_id - 1;
+    if (logical_part_id > 2) {
+      neighbors[num_neighbors++] = logical_part_id - 3;
+      neighbors[num_neighbors++] = logical_part_id - 2;
+    }
+  }
+  else {
+    if (logical_part_id + 3 < num_parts)
+      neighbors[num_neighbors++] = logical_part_id + 3;
+    if (logical_part_id > 1) {
+      neighbors[num_neighbors++] = logical_part_id - 1;
+      neighbors[num_neighbors++] = logical_part_id - 2;
+    }
+  }
+}
+
+/**\brief Test querying of part neighbors
+ *
+ * Test:
+ * - iMeshP_getNumPartNbors
+ * - iMeshP_getNumPartNborsArr
+ * - iMeshP_getPartNbors
+ * - iMeshP_getPartNborsArr
+ */
+int test_get_neighbors( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr, rank;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  
+  std::vector<iMeshP_Part> local_parts;
+  map.part_id_from_rank( rank, local_parts );
+  
+    // get handles for local parts
+  std::vector<iMeshP_PartHandle> handles(local_parts.size());
+  iMeshP_PartHandle* ptr = &handles[0];
+  int junk1 = handles.size(), junk2 = 0;
+  iMeshP_getPartHandlesFromPartsIdsArr( imesh, prtn, &local_parts[0], local_parts.size(),
+                                        &ptr, &junk1, &junk2, &ierr );
+  PCHECK;
+  assert( ptr == &handles[0] );
+  assert( junk2 == (int)handles.size() );
+  
+    // get logical ids for local parts
+  std::vector<int> logical_ids;
+  map.local_id_from_rank( rank, logical_ids );
+  
+    // get neighbors for each local part
+  std::vector< std::vector<iMeshP_Part> > neighbors( logical_ids.size() );
+  for (size_t i = 0;i < logical_ids.size(); ++i) {
+    int logical_neighbors[5], num_neighbors;
+    get_part_neighbors( logical_ids[i], map.num_parts(), logical_neighbors, num_neighbors );
+    neighbors[i].resize( num_neighbors );
+    for (int j = 0; j < num_neighbors; ++j)
+      neighbors[i][j] = map.part_id_from_local_id( logical_neighbors[j] );
+    std::sort( neighbors[i].begin(), neighbors[i].end() );
+  }
+  
+    // test iMeshP_getNumPartNbors
+  std::vector< iMeshP_Part > invalid, failed;
+  for (size_t i = 0; i < local_parts.size(); ++i) {
+    int count;
+    iMeshP_getNumPartNbors( imesh, prtn, handles[i], iBase_VERTEX, &count, &ierr );
+    if (ierr)
+      failed.push_back( local_parts[i] );
+    else if (count != (int)neighbors[i].size())
+      invalid.push_back( local_parts[i] );
+  }
+  if (!failed.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getNumPartNbors failed for " << failed.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE; PCHECK;
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getNumPartNbors was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE; PCHECK;
+  }
+  
+    // test iMeshP_getPartNbors
+  ierr = 0;
+  for (size_t i = 0; i < local_parts.size(); ++i) {
+    int count, junk = 0, another_count;
+    iMeshP_Part* list = 0;
+    iMeshP_getPartNbors( imesh, prtn, handles[i], iBase_VERTEX, &another_count, &list, &junk, &count, &ierr );
+    assert( count == another_count );
+    if (ierr)
+      failed.push_back( local_parts[i] );
+    else {
+      std::sort( list, list+count );
+      std::vector<iMeshP_Part> cpy( list, list+count );
+      if (cpy != neighbors[i])
+        invalid.push_back( local_parts[i] );
+      free(list);
+    }
+  }
+  if (!failed.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getPartNbors failed for " << failed.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getPartNbors was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  PCHECK;
+        
+    // test iMeshP_getNumPartNborsArr
+  std::vector<int> count_vect( handles.size() );
+  int* count_arr = &count_vect[0];
+  junk1 = handles.size();
+  iMeshP_getNumPartNborsArr( imesh, prtn, &handles[0], handles.size(), iBase_VERTEX,
+                             &count_arr, &junk1, &junk2, &ierr );
+  PCHECK;
+  assert( count_arr = &count_vect[0] );
+  assert( junk2 == (int)handles.size() );
+  for (size_t i = 0; i < local_parts.size(); ++i) {
+    if (count_arr[i] != (int)neighbors[i].size())
+      invalid.push_back( local_parts[i] );
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getNumPartNborsArr was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  PCHECK;
+  
+    // test iMeshP_getPartNborsArr
+  iMeshP_Part* nbor_arr = 0;
+  junk1 = 0, junk2 = handles.size();
+  int junk3 = 0, nbor_size;
+  iMeshP_getPartNborsArr( imesh, prtn, &handles[0], handles.size(), iBase_VERTEX,
+                          &count_arr, &junk1, &junk2, 
+                          &nbor_arr, &junk3, &nbor_size, 
+                          &ierr );
+  PCHECK;
+  assert( count_arr = &count_vect[0] );
+  assert( junk2 == (int)handles.size() );
+  std::vector<iMeshP_Part> all_nbors( nbor_arr, nbor_arr + nbor_size );
+  free( nbor_arr );
+  std::vector<iMeshP_Part>::iterator j = all_nbors.begin();
+  bool bad_length = false;
+  for (size_t i = 0; i < local_parts.size(); ++i) {
+    if (all_nbors.end() - j > count_arr[i]) {
+      bad_length = true;
+      break;
+    }
+    if (count_arr[i] != (int)neighbors[i].size()) {
+      invalid.push_back( local_parts[i] );
+    }
+    else {
+      std::vector<iMeshP_Part>::iterator e = j + count_arr[i];
+      std::sort( j, e );
+      if (!std::equal( j, e, neighbors[i].begin() ))
+        invalid.push_back( local_parts[i] );
+    }
+  }
+  if (bad_length)  {
+    std::cerr << "Processor " << rank << ": iMeshP_getPartNborsArr had inconsistent result array lengths." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!invalid.empty()) {
+    std::cerr << "Processor " << rank << ": iMeshP_getPartNborsArr was incorrect for " << invalid.size() << " parts." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  PCHECK;
+  
+  return 0;
+}
+
+// Determine the expected vertices on the interface between two parts.
+// Returns no vertices for non-adjacient parts and fails if both parts
+// are the same.
+// See create_mesh(..) for the assumed mesh.
+static int interface_verts( iMesh_Instance imesh,
+                            iMeshP_PartitionHandle prtn,
+                            iMeshP_PartHandle local_part,
+                            iMeshP_Part other_part,
+                            const PartMap& map,
+                            iBase_EntityHandle vtx_handles[3],
+                            int& num_vtx_handles )
+{
+  int ierr, rank;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+
+  iMeshP_Part local_id;
+  iMeshP_getPartIdFromPartHandle( imesh, prtn, local_part, &local_id, &ierr );
+  CHKERR;
+  
+  const int local_logical = map.local_id_from_part_id( local_id );
+  const int other_logical = map.local_id_from_part_id( other_part );
+  
+    // get grid of local vertices
+  
+  iBase_EntityHandle verts[3][3];
+  const double xbase = (local_id / 2) * 2;
+  const double ybase = (local_id % 2) * 2;
+  
+    // get quads in partition
+  iBase_EntityHandle quads[4], *ptr = quads;
+  int junk1 = 4, junk2;
+  iMesh_getEntities( imesh, local_part, iBase_FACE, iMesh_QUADRILATERAL, &ptr, &junk1, &junk2, &ierr );
+  CHKERR;
+  assert( ptr == quads );
+  assert( junk1 == 4 );
+  assert( junk2 == 4 );
+  
+    // get vertices in quads
+  iBase_EntityHandle conn[16];
+  int offsets[4], *off_ptr = offsets, junk3 = 4, junk4;
+  ptr = conn;
+  junk1 = 4;
+  iMesh_getEntArrAdj( imesh, quads, 4, iBase_VERTEX, 
+                      &ptr, &junk1, &junk2,
+                      &off_ptr, &junk3, &junk4,
+                      &ierr );
+  CHKERR;
+  assert( ptr == conn );
+  assert( junk1 == 16 );
+  assert( junk2 == 16 );
+  assert( off_ptr == offsets );
+  assert( junk3 == 4 );
+  assert( junk4 == 4 );
+  
+    // make unique vertex li8st
+  std::sort( conn, conn + 16 );
+  const int num_vtx = std::unique( conn, conn+16 ) - conn;
+  assert(9 == num_vtx);
+  
+    // get vertex coords
+  double coords[27], *coord_ptr = coords;
+  junk1 = 27;
+  int storage = iBase_INTERLEAVED;
+  iMesh_getVtxArrCoords( imesh, conn, 9, &storage, &coord_ptr, &junk1, &junk2, &ierr );
+  CHKERR;
+  assert( coord_ptr == coords );
+  assert( junk1 == 27 );
+  assert( junk2 == 27 );
+  
+    // use vertex coords to determine logical position
+  for (int i = 0; i < num_vtx; ++i) {
+    int x = (int)round(coords[3*i  ] - xbase);
+    int y = (int)round(coords[3*i+1] - ybase);
+    if (x < 0 || x > 2 || y < 0 || y > 2) {
+      std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+                << "  Invalid vertex coordinate: (" << coords[3*i] << ", " << coords[3*i+1]
+                << ", " << coords[3*i+2] << ")" << std::endl
+                << "  For logical partition " << local_id << std::endl;
+      return iBase_FAILURE;
+    }
+    verts[x][y] = conn[i];
+  }
+  
+  if (local_logical % 2) {
+    switch (other_logical - local_logical) {
+      case 0:
+        return iBase_FAILURE;
+      case 1: // upper right
+        vtx_handles[0] = verts[2][0];
+        num_vtx_handles = 1;
+        break;
+      case 2: // right
+        std::copy( verts[2], verts[2]+3, vtx_handles );
+        num_vtx_handles = 3;
+        break;
+      case -1: // above
+        vtx_handles[0] = verts[0][0];
+        vtx_handles[1] = verts[1][0];
+        vtx_handles[2] = verts[2][0];
+        num_vtx_handles = 3;
+        break;
+      case -2: // left
+        std::copy( verts[0], verts[0]+3, vtx_handles );
+        num_vtx_handles = 3;
+        break;
+      case -3: // upper left
+        vtx_handles[0] = verts[0][0];
+        num_vtx_handles = 1;
+        break;
+      default:
+        num_vtx_handles = 0;
+        break;
+    }
+  }
+  else {
+    switch (other_logical - local_logical) {
+      case 0:
+        return iBase_FAILURE;
+      case 1: // below
+        vtx_handles[0] = verts[0][2];
+        vtx_handles[1] = verts[1][2];
+        vtx_handles[2] = verts[2][2];
+        num_vtx_handles = 3;
+        break;
+      case 2: // right
+        std::copy( verts[2], verts[2]+3, vtx_handles );
+        num_vtx_handles = 3;
+        break;
+      case 3: // lower right
+        vtx_handles[0] = verts[2][2];
+        num_vtx_handles = 1;
+        break;
+      case -1: // lower left
+        vtx_handles[0] = verts[0][2];
+        num_vtx_handles = 1;
+        break;
+      case -2: // left
+        std::copy( verts[0], verts[0]+3, vtx_handles );
+        num_vtx_handles = 3;
+        break;
+      default:
+        num_vtx_handles = 0;
+        break;
+    }
+  }
+  
+  return iBase_SUCCESS;
+}
+       
+  
+  
+  
+
+/**\brief Test querying of part boundary entities
+ *
+ * Test:
+ * - iMeshP_getNumPartBdryEnts
+ * - iMeshP_getPartBdryEnts
+ */
+int test_get_part_boundary( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr, rank;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  
+    // get local part handles and part ids, and global part id list
+  std::vector<iMeshP_PartHandle> local_handles;
+  std::vector<iMeshP_Part> local_ids;
+  std::vector<iMeshP_Part> all_parts = map.get_parts();
+  ierr = get_local_parts( imesh, prtn, local_handles, &local_ids );
+  CHKERR;
+  
+    // for each combination of local part with any other part,
+    // check for valid function values.
+  std::vector< std::pair<iMeshP_Part,iMeshP_Part> > num_failed, num_error, list_failed, list_error, error;
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    iMeshP_PartHandle local_handle = local_handles[i];
+    iMeshP_Part local_id = local_ids[i];
+    for (std::vector<iMeshP_Part>::iterator j = all_parts.begin(); j != all_parts.end(); ++j) {
+      iMeshP_Part other_id = *j;
+      if (other_id == local_id)
+        continue;
+      
+      std::pair<iMeshP_Part,iMeshP_Part> part_pair;
+      part_pair.first = local_id;
+      part_pair.second = other_id; 
+      
+        // get expected values
+      iBase_EntityHandle shared_verts[5];
+      int num_shared_verts;
+      ierr = interface_verts( imesh, prtn, local_handle, other_id, map, shared_verts, num_shared_verts );
+      if (ierr != iBase_SUCCESS) {
+        error.push_back( part_pair );
+        continue;
+      }
+      std::sort( shared_verts, shared_verts + num_shared_verts );
+      
+        // test iMeshP_getNumPartBdryEnts
+      int count;
+      iMeshP_getNumPartBdryEnts( imesh, prtn, local_handle, iBase_VERTEX, iMesh_POINT,
+                                 other_id, &count, &ierr );
+      if (iBase_SUCCESS != ierr)
+        num_error.push_back( part_pair );
+      else if (count != num_shared_verts)
+        num_failed.push_back( part_pair );
+      
+        // test iMeshP_getPartBdryEnts
+      iBase_EntityHandle* ptr = 0;
+      int junk = 0;
+      iMeshP_getPartBdryEnts( imesh, prtn, local_handle, iBase_VERTEX, iMesh_POINT, other_id,
+                              &ptr, &junk, &count, &ierr );
+      if (iBase_SUCCESS != ierr)
+        list_error.push_back( part_pair );
+      else {
+        std::sort( ptr, ptr + count );
+        if (num_shared_verts != count ||
+            !std::equal( shared_verts, shared_verts + num_shared_verts, ptr ))
+          list_failed.push_back( part_pair );
+        free(ptr);
+      }
+    }
+  }
+  
+  if (!error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  Internal error for " << error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!num_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_getNumPartBdryEnts return error for " << num_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!list_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_getPartBdryEnts return error for " << list_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!num_failed.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_getNumPartBdryEnts return incorrect results for " << num_failed.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!list_failed.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_getPartBdryEnts return incorrect results for " << list_failed.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  return ierr;
+}
+
+/**\brief Test querying of part boundary entities
+ *
+ * Test:
+ * - iMeshP_initPartBdryEntIter
+ * - iMeshP_initPartBdryEntArrIter
+ */
+int test_part_boundary_iter( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr, rank, has_data;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+    
+    // get local part handles and part ids, and global part id list
+  std::vector<iMeshP_PartHandle> local_handles;
+  std::vector<iMeshP_Part> local_ids;
+  std::vector<iMeshP_Part> all_parts = map.get_parts();
+  ierr = get_local_parts( imesh, prtn, local_handles, &local_ids );
+  CHKERR;
+
+  std::vector< std::pair<iMeshP_Part,iMeshP_Part> > single_failed, single_error, 
+                                                   single_step_error, array_failed, 
+                                                   array_error, array_step_error;
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    iMeshP_PartHandle local_handle = local_handles[i];
+    iMeshP_Part local_id = local_ids[i];
+    for (std::vector<iMeshP_Part>::iterator j = all_parts.begin(); j != all_parts.end(); ++j) {
+      iMeshP_Part other_id = *j;
+      if (other_id == local_id)
+        continue;
+      
+      std::pair<iMeshP_Part,iMeshP_Part> part_pair;
+      part_pair.first = local_id;
+      part_pair.second = other_id; 
+      
+        // get expected values
+      iBase_EntityHandle shared_verts[5];
+      int num_shared_verts;
+      ierr = interface_verts( imesh, prtn, local_handle, other_id, map, shared_verts, num_shared_verts );
+      if (ierr != iBase_SUCCESS || 0 == num_shared_verts)
+        continue;
+      std::sort( shared_verts, shared_verts + num_shared_verts );
+  
+        // test single entity iterator
+      iMesh_EntityIterator siter;
+      iMeshP_initPartBdryEntIter( imesh, prtn, local_handle, iBase_VERTEX, iMesh_POINT,
+                                  other_id, &siter, &ierr );
+      if (ierr != iBase_SUCCESS) {
+        single_error.push_back( part_pair );
+      }
+      else {
+        std::vector<iBase_EntityHandle> results;
+        do {
+          iBase_EntityHandle handle;
+          iMesh_getNextEntIter( imesh, siter, &handle, &has_data, &ierr );
+          if (ierr != iBase_SUCCESS) {
+            single_step_error.push_back( part_pair );
+            break;
+          }
+          results.push_back(handle);
+        } while (has_data);
+      
+        std::sort( results.begin(), results.end() );
+        if ((int)results.size() != num_shared_verts ||
+            !std::equal( results.begin(), results.end(), shared_verts))
+          single_failed.push_back( part_pair );
+      }
+      iMesh_endEntIter( imesh, siter, &ierr );
+      
+        // test array iterator
+      iMesh_EntityArrIterator aiter;
+      iMeshP_initPartBdryEntArrIter( imesh, prtn, local_handle, iBase_VERTEX, iMesh_POINT,
+                                     num_shared_verts, other_id, &aiter, &ierr );
+      if (ierr != iBase_SUCCESS) {
+        array_error.push_back( part_pair );
+        continue;
+      }
+      iBase_EntityHandle results[5], *ptr = results;
+      int junk = 5, count;
+      iMesh_getNextEntArrIter( imesh, aiter, &ptr, &junk, &count, &has_data, &ierr );
+      if (ierr != iBase_SUCCESS) {
+        array_step_error.push_back( part_pair );
+        continue;
+      }
+      assert(count <= 5);
+      assert(ptr == results);
+      std::sort(results, results + count);
+      if (count != num_shared_verts || has_data ||
+          !std::equal( shared_verts, shared_verts + num_shared_verts, results ))
+        array_failed.push_back( part_pair );
+    }
+  }
+  
+  if (!single_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_initPartBdryEntIter return error for " << single_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!single_step_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMesh_getNextEntIter return error for " << single_step_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!single_failed.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_initPartBdryEntIter iterator iterated over invalid entities for " 
+              << single_failed.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  
+  if (!array_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_initPartBdryEntArrIter return error for " << array_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!array_step_error.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMesh_getNextEntArrIter return error for " << array_step_error.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  if (!array_failed.empty()) {
+    std::cerr << "Processor " << rank << ": Error at " __FILE__ ":" << __LINE__ << std::endl
+              << "  iMeshP_initPartBdryEntArrIter iterator iterated over invalid entities for " 
+              << array_failed.size() << " part pairs." << std::endl;
+    ierr = iBase_FAILURE;
+  }
+  
+  return ierr;
+}
+
+/**\brief Test adjacent entity query
+ *
+ * Test:
+ * - iMeshP_getAdjEntities
+ */
+int test_get_adjacencies( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+/**\brief Test entity iterators
+ *
+ * Test:
+ * - iMeshP_initEntIter
+ * - iMeshP_initEntArrIter
+ */
+int test_entity_iterator( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+/**\brief Test entity owner queries
+ *
+ * Test:
+ * - iMeshP_getEntOwnerPart
+ * - iMeshP_getEntOwnerPartArr
+ * - iMeshP_isEntOwner
+ * - iMeshP_isEntOwnerArr
+ */
+int test_entity_owner( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& map )
+{
+  int ierr, rank, size;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  MPI_Comm_size( MPI_COMM_WORLD, &size );
+    
+    // get local part handles and part ids
+  std::vector<iMeshP_PartHandle> local_handles;
+  std::vector<iMeshP_Part> local_ids;
+  ierr = get_local_parts( imesh, prtn, local_handles, &local_ids );
+  PCHECK;
+
+    // test iMeshP_getEntOwnerPart for quads in each part
+  std::vector<iBase_EntityHandle> all_quads;
+  std::vector<iMeshP_Part> quad_owners;
+  int invalid_count = 0;
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    std::vector<iBase_EntityHandle> quads;
+    ierr = get_entities( imesh, local_handles[0], iBase_FACE, iMesh_QUADRILATERAL, quads );
+    if (ierr)
+      break;
+    
+    for (size_t j = 0; j < quads.size(); ++j) {
+      all_quads.push_back( quads[j] );
+      quad_owners.push_back( local_ids[i] );
+      iMeshP_Part owner;
+      iMeshP_getEntOwnerPart( imesh, prtn, quads[j], &owner, &ierr );
+      if (iBase_SUCCESS != ierr)
+        break;
+      
+      if (owner != local_ids[i])
+        ++invalid_count;
+    }
+    if (iBase_SUCCESS != ierr)
+      break;
+  }
+  PCHECK;
+  ASSERT(0 == invalid_count);
+
+    // test iMeshP_getEntOwnerPartArr for quads in each part
+  invalid_count = 0;
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    std::vector<iBase_EntityHandle> quads;
+    ierr = get_entities( imesh, local_handles[0], iBase_FACE, iMesh_QUADRILATERAL, quads );
+    if (ierr)
+      break;
+    
+    std::vector<iMeshP_Part> owners(quads.size()), expected(quads.size(),local_ids[i]);
+    int junk = owners.size(), count;
+    iMeshP_Part* ptr = &owners[0];
+    iMeshP_getEntOwnerPartArr( imesh, prtn, &quads[0], quads.size(),
+                               &ptr, &junk, &count, &ierr );
+    if (ierr)
+      break;
+    assert( ptr == &owners[0] );
+    assert( junk == (int)owners.size() );
+    assert( count == (int)quads.size() );
+    if (owners != expected)
+      ++invalid_count;
+  }
+  PCHECK;
+  ASSERT(0 == invalid_count);
+
+    // get all vertices
+  iBase_EntityHandle* vtx_arr = 0;
+  int junk1 = 0, num_vtx;
+  int *junk2 = 0, junk3 = 0, junk4;
+  iMesh_getEntArrAdj( imesh, &all_quads[0], all_quads.size(), iBase_VERTEX,
+                      &vtx_arr, &junk1, &num_vtx, &junk2, &junk3, &junk4, &ierr );
+  PCHECK;
+  free(junk2);
+  std::sort( vtx_arr, vtx_arr + num_vtx );
+  num_vtx = std::unique( vtx_arr, vtx_arr + num_vtx ) - vtx_arr;
+  std::vector<iBase_EntityHandle> all_verts( vtx_arr, vtx_arr + num_vtx );
+  free(vtx_arr);
+  
+    // check consistency between iMeshP_getEntOwnerPart and iMeshP_getEntOwnerPartArr
+    // for all vertices
+  std::vector<iMeshP_Part> vert_owners( all_verts.size() );
+  junk1 = vert_owners.size();
+  iMeshP_Part* junk5 = &vert_owners[0];
+  iMeshP_getEntOwnerPartArr( imesh, prtn, &all_verts[0], all_verts.size(),
+                             &junk5, &junk1, &junk3, &ierr );
+  PCHECK;
+  assert( junk5 == &vert_owners[0] );
+  assert( junk1 == (int)vert_owners.size() );
+  assert( junk3 == (int)all_verts.size() );
+  
+  invalid_count = 0;
+  for (size_t i = 0; i < all_verts.size(); ++i) {
+    iMeshP_Part owner;
+    iMeshP_getEntOwnerPart( imesh, prtn, all_verts[i], &owner, &ierr );
+    if (iBase_SUCCESS != ierr || owner != vert_owners[i])
+      ++invalid_count;
+  }
+  ASSERT(0 == invalid_count);
+  
+    // get lists for all entities
+  std::vector<iBase_EntityHandle> all_entities(all_verts);
+  std::copy( all_quads.begin(), all_quads.end(), std::back_inserter(all_entities) );
+  std::vector<iMeshP_Part> all_owners( vert_owners );
+  std::copy( quad_owners.begin(), quad_owners.end(), std::back_inserter(all_owners) );
+    
+    // check consistency of iMeshP_isEntOwner for all entities
+  invalid_count = 0;
+  ierr = iBase_SUCCESS;
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    for (size_t j = 0; ierr == iBase_SUCCESS && j < all_entities.size(); ++j) {
+      int is_owner;
+      iMeshP_isEntOwner( imesh, prtn, local_handles[i], all_entities[j], &is_owner, &ierr );
+      if (ierr != iBase_SUCCESS)
+        break;
+      if (!is_owner == (local_ids[i] == all_owners[j]))
+        ++invalid_count;
+    }
+  }
+  PCHECK;
+  ASSERT(0 == invalid_count);
+    
+    // check consistency of iMeshP_isEntOwnerArr for all entities
+  for (size_t i = 0; i < local_handles.size(); ++i) {
+    std::vector<int> is_owner_list(all_entities.size());
+    junk1 = is_owner_list.size(); 
+    int* junk6 = &is_owner_list[0];
+    iMeshP_isEntOwnerArr( imesh, prtn, local_handles[i], &all_entities[0], all_entities.size(),
+                          &junk6, &junk1, &junk3, &ierr );
+    if (iBase_SUCCESS != ierr)
+      break;
+    assert( junk6 == &is_owner_list[0] );
+    assert( junk1 == (int)is_owner_list.size() );
+    assert( junk3 == (int)all_entities.size() );
+    invalid_count = 0;
+    for (size_t i = 0; i < all_entities.size(); ++i) {
+      if (!(is_owner_list[i]) == (local_ids[0] == all_owners[i]))
+        ++invalid_count;
+    }
+  }
+  PCHECK;
+  ASSERT(0 == invalid_count);
+  
+    
+    // check globally consistent owners for all vertices
+  
+    // first communicate total number of vertex entries to be sent to root proc
+  int local_count = all_verts.size(), global_count = 0;
+  ierr = MPI_Reduce( &local_count, &global_count, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD );
+  CHKERR;
+  
+    // for each vertex, store { (x << 2) | y, owning part id }
+  std::vector<int> vtxdata( 2 * all_verts.size() );
+  std::vector<double> coords( 3 * all_verts.size() );
+  double* junk7 = &coords[0];
+  junk1 = coords.size();
+  junk4 = iBase_INTERLEAVED;
+  iMesh_getVtxArrCoords( imesh, &all_verts[0], all_verts.size(), &junk4, &junk7, &junk1, &junk3, &ierr );
+  CHKERR;
+  assert(junk7 == &coords[0]);
+  assert(junk3 == (int)coords.size());
+  for (size_t i = 0; i < all_verts.size(); ++i) {
+    int x = (int)round(coords[3*i  ]);
+    int y = (int)round(coords[3*i+1]);
+    vtxdata[2*i  ] = (x << 2) | y;
+    vtxdata[2*i+1] = vert_owners[i];
+  }
+  
+    // collect all data on root procesor
+  std::vector<int> all_data( 2*global_count );
+  std::vector<int> displ(size), counts(size);
+  ierr = MPI_Gatherv( &vtxdata[0], vtxdata.size(), MPI_INT,
+                      &all_data[0], &counts[0], &displ[0], MPI_INT, 
+                      0, MPI_COMM_WORLD );
+  CHKERR;
+  if (rank == 0) {
+      // map from vertex tag to indices into data
+    std::multimap<int,int> data_map; 
+    for (int i = 0; i < global_count; ++i) {
+      std::pair<int,int> p;
+      p.first = all_data[2*i];
+      p.second = i;
+      data_map.insert( p );
+    }
+    
+      // check consistent data for each vtx
+    std::multimap<int,int>::const_iterator a, b;
+    for (a = data_map.begin(); a != data_map.end(); a = b) {
+      for (b = a; b != data_map.end() && a->first == b->first; ++b) {
+        int idx1 = a->second;
+        int idx2 = b->second;
+        if (all_data[2*idx1+1] == all_data[2*idx2+1])
+          continue;
+        
+        ierr = iBase_FAILURE;
+        
+        int proc1 = std::lower_bound(displ.begin(), displ.end(),2*idx1) - displ.begin();
+        if (displ[proc1] != 2*idx1)
+          ++proc1;
+        int proc2 = std::lower_bound(displ.begin(), displ.end(),2*idx2) - displ.begin();
+        if (displ[proc2] != 2*idx2)
+          ++proc2;
+        
+        std::cerr << "Error at " __FILE__ ":" << __LINE__ << " : " << std::endl
+                  << "  For vertex at (" << (a->first >> 2) << ", " << (a->first & 3) << ") :" << std::endl
+                  << "  Processor " << proc1 << " has " << all_data[2*idx1+1] << " as the owning part" << std::endl
+                  << "  Processor " << proc2 << " has " << all_data[2*idx2+1] << " as the owning part" << std::endl;
+      }
+    }
+  }
+  return ierr;
+}
+
+/**\brief Test entity status
+ *
+ * Test:
+ * - iMeshP_getEntStatus
+ * - iMeshP_getEntStatusArr
+ */
+int test_entity_status( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+/**\brief Test information about entity copies for interface entities
+ *
+ * Test:
+ * - iMeshP_getNumCopies
+ * - iMeshP_getCopyParts
+ * - iMeshP_getCopies
+ * - iMeshP_getCopyOnPart
+ * - iMeshP_getOwnerCopy
+ */
+int test_entity_copies( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+/**\brief Test creation of ghost entities
+ *
+ * Test:
+ * - iMeshP_addGhostOf
+ * - iMeshP_createGhostEnts
+ */
+int test_create_ghost_ents( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+/**\brief Test commuinication of tag data
+ *
+ * Test:
+ * - iMeshP_pushTags
+ * - iMeshP_pushTagsEnt
+ */
+int test_push_tag_data( iMesh_Instance imesh, iMeshP_PartitionHandle prtn, const PartMap& )
+{
+  return iBase_SUCCESS;
+}
+
+
+/**************************************************************************
+                          PartMap class
+ **************************************************************************/
+
+ 
+
+
+int PartMap::build_map( iMesh_Instance imesh,
+                        iMeshP_PartitionHandle prtn,
+                        int num_expected_parts )
+{
+  int ierr, rank, size;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+  MPI_Comm_size( MPI_COMM_WORLD, &size );
+  
+    // get local parts
+  std::vector<iMeshP_PartHandle> local_parts;
+  std::vector<iMeshP_Part> imesh_ids;
+  ierr = get_local_parts( imesh, prtn, local_parts, &imesh_ids );
+  CHKERR;
+  
+    // get logical ids for local parts
+  std::vector<int> local_ids(local_parts.size());
+  for (size_t i = 0; i < local_parts.size(); ++i) {
+    ierr = part_from_coords( imesh, local_parts[i], local_ids[i] );
+    CHKERR;
+  }
+  
+    // get total number of parts
+  int num_global = 0, num_local = local_parts.size();
+  ierr = MPI_Allreduce( &num_local, &num_global, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD );
+  CHKERR;
+  if (num_global != num_expected_parts) {
+    std::cerr << "Invalid/unexpected global part count at " __FILE__ ":" 
+              << __LINE__ << " (proc " << rank << "): " << std::endl
+              << "  Expected: " << num_expected_parts << std::endl
+              << "  Actual:   " << num_global << std::endl;
+    return 1;
+  }
+  
+    // get counts and displacements for Allgatherv calls
+  std::vector<int> dspls(size), counts(size);
+  ierr = MPI_Allgather( &num_local, 1, MPI_INT, &counts[0], 1, MPI_INT, MPI_COMM_WORLD );
+  CHKERR;
+  dspls[0] = 0;
+  for (int i = 1; i < size; ++i)
+    dspls[i] = dspls[i-1] + counts[i-1];
+ 
+    // gather iMeshP_Part list from each processor
+  std::vector<unsigned> global_part_ids(num_expected_parts);
+  assert(sizeof(iMeshP_Part) == sizeof(int));
+  ierr = MPI_Allgatherv( &imesh_ids[0], num_local, MPI_UNSIGNED,
+                         &global_part_ids[0], &counts[0], &dspls[0], MPI_UNSIGNED,
+                         MPI_COMM_WORLD );
+  CHKERR;
+  
+    // gather local ids from each processor
+  std::vector<int> global_id_list(num_expected_parts);
+  ierr = MPI_Allgatherv( &local_ids[0], num_local, MPI_INT,
+                         &global_id_list[0], &counts[0], &dspls[0], MPI_INT,
+                         MPI_COMM_WORLD );
+  CHKERR;
+  
+    // build owner list
+  std::vector<int> global_owners(num_expected_parts);
+  for (int i = 0; i < size; ++i) 
+    for (int j = 0; j < counts[i]; ++j)
+      global_owners[dspls[i]+j] = i;
+      
+      
+    // populate member lists
+  sortedPartList = global_part_ids;
+  std::sort( sortedPartList.begin(), sortedPartList.end() );
+  partLocalIds.resize( num_expected_parts );
+  partRanks.resize( num_expected_parts );
+  for (int i = 0; i < num_expected_parts; ++i) {
+    int idx = std::lower_bound( sortedPartList.begin(), sortedPartList.end(), global_part_ids[i] ) - sortedPartList.begin();
+    partLocalIds[idx] = global_id_list[i];
+    partRanks[idx] = global_owners[i];
+  }
+  
+    // do some consistency checking
+  if (std::unique( sortedPartList.begin(), sortedPartList.end() ) != sortedPartList.end()) {
+    if (rank == 0) {
+      std::cerr << "ERROR: Duplicate iMeshP_Part values detected at " __FILE__ ":" << __LINE__ << std::endl;
+    }
+    return 1;
+  }
+  
+    // build revesre local id map and check for duplicates
+  localIdReverseMap.clear();
+  localIdReverseMap.resize(num_expected_parts, -1);
+  for (int i = 0; i < num_expected_parts; ++i) {
+    int idx = partLocalIds[i];
+    if (localIdReverseMap[idx] != -1) {
+      if (rank == 0) {
+        std::cerr << "ERROR: Part mesh has been duplicated in multiple parts." << std::endl
+                  << "  Detected at " __FILE__ ":" << __LINE__ << std::endl
+                  << "  See PartMap::part_from_coords" << std::endl;
+      }
+      return 1;
+    }
+    if (idx >= num_expected_parts) {
+      if (rank == 0) {
+        std::cerr << "ERROR: Part mesh invalid/incorrect mesh." << std::endl
+                  << "  Detected at " __FILE__ ":" << __LINE__ << std::endl
+                  << "  See PartMap::part_from_coords" << std::endl;
+      }
+      return 1;
+    }
+      
+    localIdReverseMap[idx] = i;
+  }
+  
+  return 0;
+}
+
+
+void PartMap::part_id_from_rank( int rank, std::vector<iMeshP_Part>& parts ) const
+{
+  for (size_t i = 0; i < sortedPartList.size(); ++i)
+    if (partRanks[i] == rank)
+      parts.push_back( sortedPartList[i] ); 
+}
+  
+void PartMap::local_id_from_rank( int rank, std::vector<int>& ids ) const
+{
+  for (size_t i = 0; i < sortedPartList.size(); ++i)
+    if (partRanks[i] == rank)
+      ids.push_back( partLocalIds[i] ); 
+}
+  
+
+int PartMap::part_from_coords( iMesh_Instance imesh, iMeshP_PartHandle part, int& id )
+{
+  int ierr, rank;
+  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
+
+    // get elements
+  const int num_elem = 4;
+  iBase_EntityHandle array[num_elem];
+  iBase_EntityHandle* ptr = array;
+  int junk1 = num_elem, n = -1;
+  iMesh_getEntities( imesh, part, iBase_FACE, iMesh_QUADRILATERAL, &ptr, &junk1, &n, &ierr );
+  CHKERR;
+  assert( ptr == array );
+  assert( junk1 == num_elem );
+  if (n != num_elem) {
+    std::cerr << "Internal error at " __FILE__ ":" << __LINE__  << " (proc " 
+              << rank << "): Expected all parts to have " << num_elem 
+              << " elements.  Found one with " << n << std::endl;
+    return 1;
+  }
+  
+    // get vertices
+  iBase_EntityHandle adj_array[4*num_elem];
+  int junk2, junk3, offset_array[5];
+  ptr = adj_array;
+  junk1 = sizeof(adj_array)/sizeof(adj_array[0]);
+  junk2 = sizeof(offset_array)/sizeof(offset_array[0]);
+  int* ptr2 = offset_array;
+  iMesh_getEntArrAdj( imesh, array, num_elem, iBase_VERTEX,
+                      &ptr, &junk1, &n,
+                      &ptr2, &junk2, &junk3,
+                      &ierr );
+  CHKERR;
+  assert( ptr == adj_array );
+  assert( ptr2 == offset_array );
+  assert( junk1 == sizeof(adj_array)/sizeof(adj_array[0]) );
+  assert( junk2 == sizeof(offset_array)/sizeof(offset_array[0]) );
+  assert( n == 4*num_elem );
+  assert( offset_array[0] == 0 );
+  for (int i = 1; i < junk3; ++i)
+    assert( offset_array[i] - offset_array[i-1] == 4 );
+  
+    // find center vertex
+  iBase_EntityHandle vtx;
+  bool all_match;
+  for (int i = 0; i < 4; ++i) {
+    vtx = adj_array[i];
+    all_match = true;
+    for (int j = 1; j < 4; ++j) {
+      iBase_EntityHandle* mvtx = adj_array + 4*j;
+      int k;
+      for (k = 0; k < 4; ++k)
+        if (mvtx[k] == vtx)
+          break;
+      if (k == 4)
+        all_match = false;
+    }
+    if (all_match)
+      break;
+  }
+  assert(all_match);
+  
+    // get center vertex coordinates
+  double x, y, z;
+  iMesh_getVtxCoord( imesh, vtx, &x, &y, &z, &ierr );
+  CHKERR;
+  assert( 0.0 == z );
+  const int xi = ((int)round(x) - 1)/2;
+  const int yi = ((int)round(y) - 1)/2;
+  assert (xi >= 0);
+  assert (yi >= 0);
+  assert( fabs(x - 2*xi - 1) < 1e-12 );
+  assert( fabs(y - 2*yi - 1) < 1e-12 );
+  
+  id = 2*xi + yi;
+  return 0;
+}




More information about the moab-dev mailing list