libMesh
partitioner_test.h
Go to the documentation of this file.
1 #ifndef PARTITIONER_TEST_H
2 #define PARTITIONER_TEST_H
3 
4 #include <libmesh/distributed_mesh.h>
5 #include <libmesh/elem.h>
6 #include <libmesh/partitioner.h>
7 #include <libmesh/replicated_mesh.h>
8 #include <libmesh/mesh_generation.h>
9 
10 #include "test_comm.h"
11 #include "libmesh_cppunit.h"
12 
13 
14 #if LIBMESH_DIM > 2
15 #define PARTITIONERTEST \
16  CPPUNIT_TEST( testBuild ); \
17  CPPUNIT_TEST( testPartitionEmpty ); \
18  CPPUNIT_TEST( testPartition1 ); \
19  CPPUNIT_TEST( testPartition2 ); \
20  CPPUNIT_TEST( testPartitionNProc );
21 #else
22 #define PARTITIONERTEST
23 #endif
24 
25 using namespace libMesh;
26 
27 template <typename PartitionerSubclass, typename MeshClass>
28 class PartitionerTest : public CppUnit::TestCase {
29 protected:
30 
31  std::string libmesh_suite_name;
32 
33 public:
34  void setUp()
35  {}
36 
37  void tearDown()
38  {}
39 
40  void testBuild()
41  {
42  PartitionerSubclass myclass;
43 
44  PartitionerType type = myclass.type();
45 
46  auto new_build = Partitioner::build(type);
47 
48  CPPUNIT_ASSERT_EQUAL(new_build->type(), type);
49  }
50 
52  {
53  MeshClass mesh(*TestCommWorld);
54 
56  3, 3, 3,
57  0., 1., 0., 1., 0., 1.,
58  HEX8);
59 
60  // FIXME: Splitting meshes into more than n_proc parts currently
61  // requires us to start with a mesh entirely assigned to proc 0
62  PartitionerSubclass newpart;
63  newpart.partition(mesh, 1);
64 
65  for (auto elem : mesh.element_ptr_range())
66  CPPUNIT_ASSERT_EQUAL(elem->processor_id(), processor_id_type(0));
67 
68  for (auto node : mesh.node_ptr_range())
69  CPPUNIT_ASSERT_EQUAL(node->processor_id(), processor_id_type(0));
70 
71  // But then we can manually partition, into at most many parts as
72  // were requested.
73  newpart.partition(mesh, n_parts);
74 
75  // We expect the partitioner not to suck - every processor
76  // rank (up to n_elem()) ought to have at least one element on it.
77  const processor_id_type n_nonempty =
78  std::min(n_parts, processor_id_type(3*3*3));
79 
80  // Let's make sure we can see them all even on a DistributedMesh
81  mesh.allgather();
82 
83  processor_id_type nonempty_procs = 0;
84  for (processor_id_type p=0; p != n_nonempty; ++p)
85  {
86  const std::size_t n_elem_on_p =
87  std::distance(mesh.pid_elements_begin(p),
88  mesh.pid_elements_end(p));
89  if (n_elem_on_p)
90  nonempty_procs++;
91  }
92 
93  // Unfortunately, it turns out that our partitioners *do* suck:
94  //
95  // * Metis and ParMetis can't reliably give us more than
96  // 13 non-empty ranks on the above 27 element mesh.
97  //
98  // * Our SFC partitioners are suboptimal with 8 or more ranks for
99  // only 27 elements: the smallest integer block size for 27/8
100  // elements-per-rank that won't overflow is 4, but that only fills
101  // 7 ranks, because we don't equidistribute the underflow.
102  CPPUNIT_ASSERT(nonempty_procs >= n_nonempty ||
103  nonempty_procs >= 13 ||
104  (nonempty_procs >= 7 &&
105  n_nonempty == 8) ||
106  (nonempty_procs >= 9 &&
107  n_nonempty >= 10));
108  }
109 
111  {
112  LOG_UNIT_TEST;
113 
114  MeshClass mesh(*TestCommWorld);
115  PartitionerSubclass newpart;
116 
117  // With a 0 element mesh this should just give us 0 subpartitions
118  // regardless of n_procs
119  newpart.partition(mesh, TestCommWorld->size());
120  }
121 
123  {
124  LOG_UNIT_TEST;
125 
126  this->testPartition(1);
127  }
128 
130  {
131  LOG_UNIT_TEST;
132 
133  this->testPartition(2);
134  }
135 
137  {
138  LOG_UNIT_TEST;
139 
140  this->testPartition(TestCommWorld->size());
141  }
142 };
143 
144 #define INSTANTIATE_PARTITIONER_TEST(partitionersubclass, meshclass) \
145  class PartitionerTest_##partitionersubclass##_##meshclass : \
146  public PartitionerTest<partitionersubclass, meshclass> { \
147  public: \
148  PartitionerTest_##partitionersubclass##_##meshclass() : \
149  PartitionerTest<partitionersubclass,meshclass>() { \
150  if (unitlog->summarized_logs_enabled()) \
151  this->libmesh_suite_name = "PartitionerTest"; \
152  else \
153  this->libmesh_suite_name = "PartitionerTest_" #partitionersubclass "_" #meshclass; \
154  } \
155  CPPUNIT_TEST_SUITE( PartitionerTest_##partitionersubclass##_##meshclass); \
156  PARTITIONERTEST \
157  CPPUNIT_TEST_SUITE_END(); \
158  }; \
159  \
160  CPPUNIT_TEST_SUITE_REGISTRATION( PartitionerTest_##partitionersubclass##_##meshclass );
161 
162 #endif
libMesh::Parallel::Communicator * TestCommWorld
Definition: driver.C:171
virtual void allgather()
Gathers all elements and nodes of the mesh onto every processor.
Definition: mesh_base.h:240
MeshBase & mesh
The libMesh namespace provides an interface to certain functionality in the library.
Real distance(const Point &p)
uint8_t processor_id_type
Definition: id_types.h:104
processor_id_type size() const
uint8_t processor_id_type
PartitionerType
Defines an enum for mesh partitioner types.
static std::unique_ptr< Partitioner > build(const PartitionerType solver_package)
Builds a Partitioner of the type specified by partitioner_type.
Definition: partitioner.C:159
std::string libmesh_suite_name
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
void testPartition(processor_id_type n_parts)