Sierra Toolkit  Version of the Day
UnitTestBulkModification.cpp
1 /*------------------------------------------------------------------------*/
2 /* Copyright 2010 Sandia Corporation. */
3 /* Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive */
4 /* license for use of this work by or on behalf of the U.S. Government. */
5 /* Export of this program may require a license from the */
6 /* United States Government. */
7 /*------------------------------------------------------------------------*/
8 
9 
10 #include <stk_util/unit_test_support/stk_utest_macros.hpp>
11 #include <Shards_BasicTopologies.hpp>
12 
13 #include <stk_util/parallel/Parallel.hpp>
14 
15 #include <stk_mesh/base/MetaData.hpp>
16 #include <stk_mesh/base/BulkData.hpp>
17 #include <stk_mesh/base/Entity.hpp>
18 #include <stk_mesh/base/BulkModification.hpp>
19 #include <stk_mesh/base/GetEntities.hpp>
20 #include <stk_mesh/base/Selector.hpp>
21 #include <stk_mesh/base/GetBuckets.hpp>
22 
23 #include <stk_mesh/fem/FEMMetaData.hpp>
24 
25 #include <stk_mesh/fixtures/RingFixture.hpp>
26 
27 #include <algorithm>
28 #include <stdexcept>
29 
32 using stk_classic::mesh::BucketIterator;
34 using stk_classic::mesh::EntityRank;
36 
37 class UnitTestStkMeshBulkModification {
38  public:
39  UnitTestStkMeshBulkModification(stk_classic::ParallelMachine pm) :
40  m_comm(pm),
41  m_num_procs(stk_classic::parallel_machine_size( m_comm )),
42  m_rank(stk_classic::parallel_machine_rank( m_comm )),
43  m_ring_mesh(pm)
44  { }
45 
46  void test_bulkdata_not_syncronized();
47  void test_closure_of_non_locally_used_entities();
48  void test_all_local_nodes();
49  void test_all_local_edges();
50  void test_parallel_consistency();
51 
52  BulkData& initialize_ring_fixture()
53  {
54  m_ring_mesh.m_meta_data.commit();
55  BulkData& bulk_data = m_ring_mesh.m_bulk_data;
56 
57  bulk_data.modification_begin();
58  m_ring_mesh.generate_mesh( );
59  ThrowRequire(bulk_data.modification_end());
60 
61  bulk_data.modification_begin();
62  m_ring_mesh.fixup_node_ownership( );
63  ThrowRequire(bulk_data.modification_end());
64 
65  return bulk_data;
66  }
67 
69  int m_num_procs;
70  int m_rank;
71  RingFixture m_ring_mesh;
72 };
73 
74 namespace {
75 
76 const EntityRank NODE_RANK = stk_classic::mesh::fem::FEMMetaData::NODE_RANK;
77 
78 STKUNIT_UNIT_TEST( UnitTestBulkDataNotSyrncronized , testUnit )
79 {
80  UnitTestStkMeshBulkModification unit(MPI_COMM_WORLD);
81  unit.test_bulkdata_not_syncronized();
82 }
83 
84 STKUNIT_UNIT_TEST( UnitTestClosureOfNonLocallyUsedEntities , testUnit )
85 {
86  UnitTestStkMeshBulkModification unit(MPI_COMM_WORLD);
87  unit.test_closure_of_non_locally_used_entities();
88 }
89 
90 STKUNIT_UNIT_TEST( UnitTestAllLocalNodes , testUnit )
91 {
92  UnitTestStkMeshBulkModification unit(MPI_COMM_WORLD);
93  unit.test_all_local_nodes();
94 }
95 
96 STKUNIT_UNIT_TEST( UnitTestAllLocalEdges , testUnit )
97 {
98  UnitTestStkMeshBulkModification unit(MPI_COMM_WORLD);
99  unit.test_all_local_edges();
100 }
101 
102 STKUNIT_UNIT_TEST( UnitTestParallelConsistency , testUnit )
103 {
104  UnitTestStkMeshBulkModification unit(MPI_COMM_WORLD);
105  unit.test_parallel_consistency();
106 }
107 
108 } //end namespace
109 
110 void UnitTestStkMeshBulkModification::test_bulkdata_not_syncronized()
111 {
112  BulkData& bulk_data = initialize_ring_fixture();
113 
114  bulk_data.modification_begin(); // Intentially make things unsynced
115 
116  std::vector< Entity *> entities;
117  std::vector< Entity *> entities_closure;
118  STKUNIT_ASSERT_THROW(stk_classic::mesh::find_closure(bulk_data, entities, entities_closure), std::runtime_error);
119 }
120 
121 void UnitTestStkMeshBulkModification::test_closure_of_non_locally_used_entities()
122 {
123  BulkData& bulk_data = initialize_ring_fixture();
124 
125  const stk_classic::mesh::Ghosting & ghost = bulk_data.shared_aura();
126 
127  std::vector< Entity* > ghost_receive ;
128 
129  ghost.receive_list( ghost_receive );
130 
131  if (!ghost_receive.empty()) {
132  std::vector< Entity *> entities;
133  std::vector< Entity *> entities_closure;
134 
135  entities.push_back(ghost_receive.front());
136 
137  STKUNIT_ASSERT_THROW(stk_classic::mesh::find_closure(bulk_data, entities, entities_closure), std::runtime_error);
138  }
139 }
140 
141 void UnitTestStkMeshBulkModification::test_all_local_nodes()
142 {
143  BulkData& bulk_data = initialize_ring_fixture();
144 
145  {
146  std::vector< Entity *> entities;
147  std::vector< Entity *> entities_closure;
148  find_closure(bulk_data, entities, entities_closure);
149 
150  // the closure of the an empty set of entities on all procs should be empty
151  STKUNIT_EXPECT_TRUE(entities_closure.empty());
152  }
153 
154  {
155  // Get a selector for the univeral part (contains local, shared, and ghosted)
156  const stk_classic::mesh::Part& universal = m_ring_mesh.m_meta_data.universal_part();
157  stk_classic::mesh::Selector universal_selector(universal);
158 
159  // Get the buckets that will give us the universal nodes
160  const std::vector<Bucket*>& node_buckets = bulk_data.buckets(NODE_RANK);
161  std::vector<Bucket*> buckets;
162  stk_classic::mesh::get_buckets(universal_selector, node_buckets, buckets);
163 
164  // Get the universal nodes
165  std::vector< Entity *> universal_entities;
166  for (std::vector<Bucket*>::iterator itr = buckets.begin();
167  itr != buckets.end(); ++itr) {
168  Bucket& b = **itr;
169  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
170  universal_entities.push_back(&(*bitr));
171  }
172  }
173  buckets.clear();
174 
175  // sort and unique the universal nodes
176  std::sort(universal_entities.begin(), universal_entities.end(), stk_classic::mesh::EntityLess());
177  std::vector<Entity*>::iterator new_end = std::unique(universal_entities.begin(), universal_entities.end(), stk_classic::mesh::EntityEqual());
178  universal_entities.erase(new_end, universal_entities.end());
179 
180  // Get the buckets that will give us the locally used nodes
181  stk_classic::mesh::Selector locally_used_selector =
182  m_ring_mesh.m_meta_data.locally_owned_part() |
183  m_ring_mesh.m_meta_data.globally_shared_part();
184 
185  stk_classic::mesh::get_buckets(locally_used_selector, node_buckets, buckets);
186 
187  // Get the locally used nodes
188  std::vector< Entity *> entities;
189  for (std::vector<Bucket*>::iterator itr = buckets.begin();
190  itr != buckets.end(); ++itr) {
191  Bucket& b = **itr;
192  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
193  entities.push_back(&(*bitr));
194  }
195  }
196 
197  // Get the closure, passing in the locally used nodes on each proc
198  std::vector< Entity *> entities_closure;
199  stk_classic::mesh::find_closure(bulk_data, entities, entities_closure);
200 
201  // The ghosted nodes on this part will be locally used on one of the other
202  // procs, so we expect that they will be part of the closure. In other
203  // words, the set of nodes returned by find_closure should exactly match
204  // the set of universal nodes.
205  STKUNIT_ASSERT_TRUE(universal_entities.size() == entities_closure.size());
206  stk_classic::mesh::EntityEqual ee;
207  for (size_t i = 0; i < entities_closure.size(); ++i) {
208  STKUNIT_EXPECT_TRUE(ee(universal_entities[i], entities_closure[i]));
209  }
210  }
211 }
212 
213 void UnitTestStkMeshBulkModification::test_all_local_edges()
214 {
215  BulkData& bulk_data = initialize_ring_fixture();
216  const stk_classic::mesh::EntityRank element_rank = m_ring_mesh.m_meta_data.element_rank();
217 
218  {
219  const stk_classic::mesh::Part& universal = m_ring_mesh.m_meta_data.universal_part();
220  stk_classic::mesh::Selector universal_selector(universal);
221 
222  const std::vector<Bucket*>& node_buckets = bulk_data.buckets(NODE_RANK);
223  const std::vector<Bucket*>& edge_buckets = bulk_data.buckets(element_rank);
224  std::vector<Bucket*> buckets;
225 
226  stk_classic::mesh::get_buckets(universal_selector, node_buckets, buckets);
227 
228  // get all the nodes that this process knows about
229  std::vector< Entity *> universal_entities;
230  for (std::vector<Bucket*>::iterator itr = buckets.begin();
231  itr != buckets.end(); ++itr) {
232  Bucket& b = **itr;
233  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
234  universal_entities.push_back(&(*bitr));
235  }
236  }
237  buckets.clear();
238 
239  stk_classic::mesh::get_buckets(universal_selector, edge_buckets, buckets);
240 
241  // get all the edges that this process knows about
242  for (std::vector<Bucket*>::iterator itr = buckets.begin();
243  itr != buckets.end(); ++itr) {
244  Bucket& b = **itr;
245  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
246  universal_entities.push_back(&(*bitr));
247  }
248  }
249  buckets.clear();
250 
251  // universal entities should now have all the universal nodes and edges
252  // sort and uniq the universal nodes/edges
253  std::sort(universal_entities.begin(), universal_entities.end(), stk_classic::mesh::EntityLess());
254  std::vector<Entity*>::iterator new_end = std::unique(universal_entities.begin(), universal_entities.end(), stk_classic::mesh::EntityEqual());
255  universal_entities.erase(new_end, universal_entities.end());
256 
257  // get the buckets that we need to traverse to get the locally used edges
258  stk_classic::mesh::Selector locally_used_selector =
259  m_ring_mesh.m_meta_data.locally_owned_part() |
260  m_ring_mesh.m_meta_data.globally_shared_part();
261 
262  stk_classic::mesh::get_buckets(locally_used_selector, edge_buckets, buckets);
263 
264  // get the locally used edges and store them in entities
265  std::vector< Entity *> entities;
266  for (std::vector<Bucket*>::iterator itr = buckets.begin();
267  itr != buckets.end(); ++itr) {
268  Bucket& b = **itr;
269  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
270  entities.push_back(&(*bitr));
271  }
272  }
273 
274  // call find_closure, passing in the locally used edges
275  std::vector< Entity *> entities_closure;
276  stk_classic::mesh::find_closure(bulk_data, entities, entities_closure);
277 
278  // The ghosted entities on this proc (edge or node) should be contained
279  // in the closure of the locally-used edge on some other proc, so we
280  // expect that they will be part of the closure. In other
281  // words, the set of entities returned by find_closure should exactly match
282  // the set of universal entities (nodes and edges).
283  STKUNIT_ASSERT_TRUE(universal_entities.size() == entities_closure.size());
284  stk_classic::mesh::EntityEqual ee;
285  for (size_t i = 0; i < entities_closure.size(); ++i) {
286  STKUNIT_EXPECT_TRUE(ee(universal_entities[i], entities_closure[i]));
287  }
288  }
289 }
290 
291 void UnitTestStkMeshBulkModification::test_parallel_consistency()
292 {
293  BulkData& bulk_data = initialize_ring_fixture();
294 
295  stk_classic::CommBroadcast all(bulk_data.parallel(), 0);
296 
297  std::vector< Entity *> entities;
298  std::vector< Entity *> entities_closure;
299 
300  // For proc 0 only, add locally used nodes to entities, for all other
301  // procs, leave entities empty.
302  if (m_rank == 0) {
303  const std::vector<Bucket*>& node_buckets = bulk_data.buckets(NODE_RANK);
304 
305  stk_classic::mesh::Selector locally_used_selector =
306  m_ring_mesh.m_meta_data.locally_owned_part() |
307  m_ring_mesh.m_meta_data.globally_shared_part();
308 
309  std::vector<Bucket*> buckets;
310  stk_classic::mesh::get_buckets(locally_used_selector, node_buckets, buckets);
311 
312  for (std::vector<Bucket*>::iterator itr = buckets.begin();
313  itr != buckets.end(); ++itr) {
314  Bucket& b = **itr;
315  for (BucketIterator bitr = b.begin(); bitr != b.end(); ++bitr) {
316  entities.push_back(&(*bitr));
317  }
318  }
319  }
320 
321  // Call find_closure with proc 0 passing in locally-used nodes
322  stk_classic::mesh::find_closure(bulk_data, entities, entities_closure);
323 
324  // Proc 0 will broadcast the global ids of the nodes it passed to
325  // find_closure
326 
327  // pack entities for sizing
328  for (std::vector<Entity*>::const_iterator
329  ep = entities.begin() ; ep != entities.end() ; ++ep ) {
330  all.send_buffer().pack<stk_classic::mesh::EntityKey>((*ep)->key());
331  }
332 
333  all.allocate_buffer();
334 
335  // pack for real
336  for (std::vector<Entity*>::const_iterator
337  ep = entities.begin() ; ep != entities.end() ; ++ep ) {
338  all.send_buffer().pack<stk_classic::mesh::EntityKey>((*ep)->key());
339  }
340 
341  all.communicate();
342 
343  // clear-out entities and put the nodes that correspond to the keys
344  // broadcast by proc 0 into entities.
345  entities.clear();
346  stk_classic::CommBuffer& buf = all.recv_buffer();
348  while ( buf.remaining() ) {
349  buf.unpack<stk_classic::mesh::EntityKey>(k);
350  Entity * e = bulk_data.get_entity(k);
351  // If a proc is not aware of a key, that means it has no relationship
352  // with that entity, so it can ignore it.
353  if (e != NULL) {
354  entities.push_back(e);
355  }
356  }
357 
358  // sort and unique entities
359  std::sort(entities.begin(), entities.end(), stk_classic::mesh::EntityLess());
360  std::vector<Entity*>::iterator new_end = std::unique(entities.begin(), entities.end(), stk_classic::mesh::EntityEqual());
361  entities.erase(new_end, entities.end());
362 
363  // If any processor had ghosted nodes that were local to proc 0, those
364  // nodes should be in the closure because proc 0 passed them in to
365  // find_closure.
366  STKUNIT_ASSERT_TRUE(entities.size() == entities_closure.size());
367  stk_classic::mesh::EntityEqual ee;
368  for (size_t i = 0; i < entities_closure.size(); ++i) {
369  STKUNIT_EXPECT_TRUE(ee(entities[i], entities_closure[i]));
370  }
371 }
Comparison operator for entities compares the entities&#39; keys.
Definition: Entity.hpp:478
Data for ghosting mesh entities.
Definition: Ghosting.hpp:28
This is a class for selecting buckets based on a set of meshparts and set logic.
Definition: Selector.hpp:112
const std::vector< Bucket * > & buckets(EntityRank rank) const
Query all buckets of a given entity rank.
Definition: BulkData.hpp:195
Ghosting & shared_aura() const
Query the shared-entity aura. Is likely to be stale if ownership or sharing has changed and the &#39;modi...
Definition: BulkData.hpp:375
Entity * get_entity(EntityRank entity_rank, EntityId entity_id) const
Get entity with a given key.
Definition: BulkData.hpp:211
Integer type for the entity keys, which is an encoding of the entity type and entity identifier...
An application-defined subset of a problem domain.
Definition: Part.hpp:49
unsigned parallel_machine_rank(ParallelMachine parallel_machine)
Member function parallel_machine_rank ...
Definition: Parallel.cpp:29
ParallelMachine parallel() const
The parallel machine.
Definition: BulkData.hpp:79
void receive_list(std::vector< Entity * > &) const
Entities ghosted on this processor from the owner.
Definition: Ghosting.cpp:33
bool modification_end()
Parallel synchronization of modifications and transition to the guaranteed parallel consistent state...
bool modification_begin()
Begin a modification phase during which the mesh bulk data could become parallel inconsistent. This is a parallel synchronous call. The first time this method is called the mesh meta data is verified to be committed and parallel consistent. An exception is thrown if this verification fails.
Definition: BulkData.cpp:172
iterator begin() const
Beginning of the bucket.
Definition: Bucket.hpp:113
iterator end() const
End of the bucket.
Definition: Bucket.hpp:116
unsigned parallel_machine_size(ParallelMachine parallel_machine)
Member function parallel_machine_size ...
Definition: Parallel.cpp:18
Manager for an integrated collection of entities, entity relations, and buckets of field data...
Definition: BulkData.hpp:49
A fundamental unit within the discretization of a problem domain, including but not limited to nodes...
Definition: Entity.hpp:120
Sierra Toolkit.
MPI_Comm ParallelMachine
Definition: Parallel.hpp:32
AllSelectedBucketsRange get_buckets(const Selector &selector, const BulkData &mesh)
Definition: GetBuckets.cpp:26
A container for the field data of a homogeneous collection of entities.
Definition: Bucket.hpp:94