Sierra Toolkit  Version of the Day
UnitTestBulkData.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 <iostream>
11 #include <sstream>
12 #include <stdexcept>
13 
14 #include <stk_util/unit_test_support/stk_utest_macros.hpp>
15 
16 #include <stk_util/parallel/Parallel.hpp>
17 
18 #include <stk_mesh/base/BulkData.hpp>
19 #include <stk_mesh/base/GetEntities.hpp>
20 #include <stk_mesh/base/EntityComm.hpp>
21 #include <stk_mesh/base/Comm.hpp>
22 
23 #include <stk_mesh/fixtures/BoxFixture.hpp>
24 #include <stk_mesh/fixtures/RingFixture.hpp>
25 
26 #include <unit_tests/UnitTestModificationEndWrapper.hpp>
27 #include <unit_tests/UnitTestRingFixture.hpp>
28 
35 using stk_classic::mesh::BaseEntityRank;
39 using stk_classic::mesh::EntityId;
41 using stk_classic::mesh::EntityVector;
42 using stk_classic::mesh::EntityRank;
45 
46 namespace {
47 
48 const EntityRank NODE_RANK = FEMMetaData::NODE_RANK;
49 
50 void donate_one_element( BulkData & mesh , bool aura )
51 {
52  const unsigned p_rank = mesh.parallel_rank();
53 
54  Selector select_owned( MetaData::get(mesh).locally_owned_part() );
55 
56  std::vector<unsigned> before_count ;
57  std::vector<unsigned> after_count ;
58 
59  count_entities( select_owned , mesh , before_count );
60 
61  // Change owner of an element on a process boundary
62  // from P0 to P1, and then recount to confirm ownership change
63 
64  std::vector<EntityProc> change ;
65 
66  // A shared node:
67  Entity * node = NULL ;
68  Entity * elem = NULL ;
69 
70  for ( std::vector<Entity*>::const_iterator
71  i = mesh.entity_comm().begin() ;
72  i != mesh.entity_comm().end() ; ++i ) {
73  if ( in_shared( **i ) && (**i).entity_rank() == BaseEntityRank ) {
74  node = *i ;
75  break ;
76  }
77  }
78 
79  STKUNIT_ASSERT( node != NULL );
80 
81  for ( PairIterRelation rel = node->relations( 3 );
82  ! rel.empty() && elem == NULL ; ++rel ) {
83  elem = rel->entity();
84  if ( elem->owner_rank() != p_rank ) { elem = NULL ; }
85  }
86 
87  STKUNIT_ASSERT( elem != NULL );
88 
89  unsigned donated_nodes = 0 ;
90 
91  // Only process #0 donates an element and its owned nodes:
92  if ( 0 == p_rank ) {
93  EntityProc entry ;
94  entry.first = elem ;
95  entry.second = node->sharing()[0].proc ;
96  change.push_back( entry );
97  for ( PairIterRelation
98  rel = elem->relations(0) ; ! rel.empty() ; ++rel ) {
99  if ( rel->entity()->owner_rank() == p_rank ) {
100  entry.first = rel->entity();
101  change.push_back( entry );
102  ++donated_nodes ;
103  }
104  }
105  }
106 
107  STKUNIT_ASSERT( mesh.modification_begin() );
108  mesh.change_entity_owner( change );
109  STKUNIT_ASSERT( stk_classic::unit_test::modification_end_wrapper( mesh , aura ) );
110 
111  count_entities( select_owned , mesh , after_count );
112 
113  if ( 0 == p_rank ) {
114  STKUNIT_ASSERT_EQUAL( before_count[3] - 1 , after_count[3] );
115  STKUNIT_ASSERT_EQUAL( before_count[0] - donated_nodes, after_count[0] );
116  }
117 }
118 
119 void donate_all_shared_nodes( BulkData & mesh , bool aura )
120 {
121  const unsigned p_rank = mesh.parallel_rank();
122 
123  const Selector select_used = MetaData::get(mesh).locally_owned_part() |
124  MetaData::get(mesh).globally_shared_part() ;
125 
126  std::vector<unsigned> before_count ;
127  std::vector<unsigned> after_count ;
128 
129  count_entities( select_used , mesh , before_count );
130 
131  // Donate owned shared nodes to first sharing process.
132 
133  const std::vector<Entity*> & entity_comm = mesh.entity_comm();
134 
135  STKUNIT_ASSERT( ! entity_comm.empty() );
136 
137  std::vector<EntityProc> change ;
138 
139  for ( std::vector<Entity*>::const_iterator
140  i = entity_comm.begin() ;
141  i != entity_comm.end() &&
142  (**i).entity_rank() == BaseEntityRank ; ++i ) {
143  Entity * const node = *i ;
145 
146  if ( node->owner_rank() == p_rank && ! ec.empty() ) {
147  change.push_back( EntityProc( node , ec->proc ) );
148  }
149  }
150 
151  STKUNIT_ASSERT( mesh.modification_begin() );
152  mesh.change_entity_owner( change );
153  STKUNIT_ASSERT( stk_classic::unit_test::modification_end_wrapper( mesh , aura ) );
154 
155  count_entities( select_used , mesh , after_count );
156 
157  STKUNIT_ASSERT( 3 <= after_count.size() );
158  STKUNIT_ASSERT_EQUAL( before_count[0] , after_count[0] );
159  STKUNIT_ASSERT_EQUAL( before_count[1] , after_count[1] );
160  STKUNIT_ASSERT_EQUAL( before_count[2] , after_count[2] );
161  STKUNIT_ASSERT_EQUAL( before_count[3] , after_count[3] );
162 }
163 
164 } // empty namespace
165 
166 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testBulkData)
167 {
168  // Unit test the Part functionality in isolation:
169 
170  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
171  MPI_Barrier( pm );
172 
173  std::vector<std::string> entity_names(10);
174  for ( size_t i = 0 ; i < 10 ; ++i ) {
175  std::ostringstream name ;
176  name << "EntityRank" << i ;
177  entity_names[i] = name.str();
178  }
179 
180  MetaData meta( entity_names );
181 
182  meta.commit();
183 
184  BulkData bulk( meta , pm , 100 );
185 
186  for ( size_t i = 0 ; i < 4 ; ++i ) {
187  STKUNIT_ASSERT( bulk.modification_begin() );
188  STKUNIT_ASSERT_EQUAL( i , bulk.synchronized_count() );
189  STKUNIT_ASSERT( bulk.modification_end() );
190  }
191 
192  std::vector<Part*> no_parts ;
193 
194  Entity * e[10] ;
195 
196  const unsigned id = bulk.parallel_rank() + 1 ;
197 
198  STKUNIT_ASSERT( bulk.modification_begin() );
199  for ( size_t i = 0 ; i < 10 ; ++i ) {
200  e[i] = & bulk.declare_entity( i , id , no_parts );
201  }
202  STKUNIT_ASSERT( bulk.modification_end() );
203 
204  for ( size_t i = 0 ; i < 10 ; ++i ) {
205  STKUNIT_ASSERT( e[i] == bulk.get_entity( i , id ) );
206  }
207 
208  STKUNIT_ASSERT( bulk.modification_begin() );
209  STKUNIT_ASSERT_THROW( bulk.declare_entity( 11 , id , no_parts ),
210  std::logic_error );
211  STKUNIT_ASSERT( bulk.modification_end() );
212 
213  // Catch not-ok-to-modify
214  STKUNIT_ASSERT_THROW( bulk.declare_entity( 0 , id + 1 , no_parts ),
215  std::logic_error );
216 }
217 
218 //----------------------------------------------------------------------
219 // Testing for mesh entities without relations
220 
221 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeOwner_nodes)
222 {
223  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
224  MPI_Barrier( pm );
225 
226  enum { nPerProc = 10 };
227  const unsigned p_rank = stk_classic::parallel_machine_rank( pm );
228  const unsigned p_size = stk_classic::parallel_machine_size( pm );
229  const unsigned id_total = nPerProc * p_size ;
230  const unsigned id_begin = nPerProc * p_rank ;
231  const unsigned id_end = nPerProc * ( p_rank + 1 );
232 
233  const int spatial_dimension = 3;
234  MetaData meta( stk_classic::mesh::fem::entity_rank_names(spatial_dimension) );
235  BulkData bulk( meta , pm , 100 );
236 
237  const PartVector no_parts ;
238 
239  meta.commit();
240  bulk.modification_begin();
241 
242  // Ids for all entities (all entities have type 0):
243 
244  std::vector<EntityId> ids( id_total );
245 
246  for ( unsigned i = 0 ; i < id_total ; ++i ) {
247  ids[i] = i + 1;
248  }
249 
250  // Declare just those entities in my range of ids:
251 
252  for ( unsigned i = id_begin ; i < id_end ; ++i ) {
253  bulk.declare_entity( 0 , ids[i] , no_parts );
254  }
255 
256  STKUNIT_ASSERT( bulk.modification_end() );
257 
258  // Verify that I only have entities in my range:
259 
260  for ( unsigned i = 0 ; i < id_total ; ++i ) {
261  Entity * e = bulk.get_entity( 0 , ids[ i ] );
262  if ( id_begin <= i && i < id_end ) {
263  STKUNIT_ASSERT( NULL != e );
264  }
265  else {
266  STKUNIT_ASSERT( NULL == e );
267  }
268  }
269 
270  // Test change owner no-op first:
271 
272  std::vector<EntityProc> change ;
273 
274  STKUNIT_ASSERT( bulk.modification_begin() );
275  bulk.change_entity_owner( change );
276  STKUNIT_ASSERT( bulk.modification_end() );
277 
278  for ( unsigned i = 0 ; i < id_total ; ++i ) {
279  Entity * e = bulk.get_entity( 0 , ids[ i ] );
280  if ( id_begin <= i && i < id_end ) {
281  STKUNIT_ASSERT( NULL != e );
282  }
283  else {
284  STKUNIT_ASSERT( NULL == e );
285  }
286  }
287 
288  // Can only test changing owner in parallel.
289 
290  if ( 1 < p_size ) {
291  // Give my last two ids to the next process
292  // Get the previous process' last two ids
293 
294  const unsigned p_give = ( p_rank + 1 ) % p_size ;
295  const unsigned id_give = id_end - 2 ;
296  const unsigned id_get = ( id_begin + id_total - 2 ) % id_total ;
297 
298  STKUNIT_ASSERT( NULL != bulk.get_entity( 0 , ids[id_give] ) );
299  STKUNIT_ASSERT( NULL != bulk.get_entity( 0 , ids[id_give+1] ) );
300  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_get] ) );
301  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_get+1] ) );
302 
303  change.resize(2);
304  change[0].first = bulk.get_entity( 0 , ids[id_give] );
305  change[0].second = p_give ;
306  change[1].first = bulk.get_entity( 0 , ids[id_give+1] );
307  change[1].second = p_give ;
308 
309  STKUNIT_ASSERT( bulk.modification_begin() );
310  bulk.change_entity_owner( change );
311  STKUNIT_ASSERT( bulk.modification_end() );
312 
313  STKUNIT_ASSERT( NULL != bulk.get_entity( 0 , ids[id_get] ) );
314  STKUNIT_ASSERT( NULL != bulk.get_entity( 0 , ids[id_get+1] ) );
315 
316  // Entities given away are destroyed until the next modification cycle
317  {
318  Entity * const e0 = bulk.get_entity( 0 , ids[id_give] );
319  Entity * const e1 = bulk.get_entity( 0 , ids[id_give+1] );
320  STKUNIT_ASSERT( NULL != e0 && e0->bucket().capacity() == 0 );
321  STKUNIT_ASSERT( NULL != e1 && e1->bucket().capacity() == 0 );
322  }
323 
324  STKUNIT_ASSERT( bulk.modification_begin() );
325  STKUNIT_ASSERT( bulk.modification_end() );
326 
327  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_give] ) );
328  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_give+1] ) );
329  }
330 }
331 
332 //----------------------------------------------------------------------
333 // Testing for creating existing mesh entities without relations
334 
335 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testCreateMore)
336 {
337  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
338  MPI_Barrier( pm );
339 
340  enum { nPerProc = 10 };
341 
342  const unsigned p_size = stk_classic::parallel_machine_size( pm );
343  const unsigned p_rank = stk_classic::parallel_machine_rank( pm );
344 
345  if ( 1 < p_size ) {
346 
347  const unsigned id_total = nPerProc * p_size ;
348  const unsigned id_begin = nPerProc * p_rank ;
349  const unsigned id_end = nPerProc * ( p_rank + 1 );
350 
351  const int spatial_dimension = 3;
352  MetaData meta( stk_classic::mesh::fem::entity_rank_names(spatial_dimension) );
353 
354  const PartVector no_parts ;
355 
356  meta.commit();
357 
358  BulkData bulk( meta , pm , 100 );
359 
360  bulk.modification_begin();
361 
362  // Ids for all entities (all entities have type 0):
363 
364  std::vector<EntityId> ids( id_total );
365 
366  for ( unsigned i = 0 ; i < id_total ; ++i ) { ids[i] = i + 1; }
367 
368  // Declare just those entities in my range of ids:
369 
370  for ( unsigned i = id_begin ; i < id_end ; ++i ) {
371  bulk.declare_entity( 0 , ids[i] , no_parts );
372  }
373 
374  STKUNIT_ASSERT( bulk.modification_end() );
375 
376  // Only one process create entities with previous process' last two ids
377 
378  const unsigned id_get = ( id_begin + id_total - 2 ) % id_total ;
379 
380  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_get] ) );
381  STKUNIT_ASSERT( NULL == bulk.get_entity( 0 , ids[id_get+1] ) );
382 
383  STKUNIT_ASSERT( bulk.modification_begin() );
384 
385  if ( 1 == p_rank ) {
386  // These declarations create entities that already exist,
387  // which will be an error. Must create an owned entity
388  // to use them, thus they become shared.
389 
390  Entity & e0 = bulk.declare_entity( 0 , ids[ id_get ] , no_parts );
391  Entity & e1 = bulk.declare_entity( 0 , ids[ id_get + 1 ] , no_parts );
392 
393  Entity & eU = bulk.declare_entity( 1 , 1 , no_parts );
394 
395  bulk.declare_relation( eU , e0 , 0 );
396  bulk.declare_relation( eU , e1 , 1 );
397  }
398 
399  bulk.modification_end();
400 
401  if ( 1 == p_rank ) {
402  Entity * e0 = bulk.get_entity( 0 , ids[id_get] );
403  Entity * e1 = bulk.get_entity( 0 , ids[id_get+1] );
404  STKUNIT_ASSERT( NULL != e0 );
405  STKUNIT_ASSERT( NULL != e1 );
406  STKUNIT_ASSERT( 0 == e0->owner_rank() );
407  STKUNIT_ASSERT( 0 == e1->owner_rank() );
408  }
409 
410  // Now test tripping the error condition
411 
412  bulk.modification_begin();
413 
414  if ( 0 == p_rank ) {
415  bulk.declare_entity( 0 , ids[ id_get ] , no_parts );
416  bulk.declare_entity( 0 , ids[ id_get + 1 ] , no_parts );
417  }
418 
419  STKUNIT_ASSERT_THROW( bulk.modification_end() , std::runtime_error );
420  }
421 }
422 
423 //----------------------------------------------------------------------
424 //----------------------------------------------------------------------
425 
426 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeOwner_ring)
427 {
428  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
429  MPI_Barrier( pm );
430 
431  enum { nPerProc = 10 };
432  const unsigned p_rank = stk_classic::parallel_machine_rank( pm );
433  const unsigned p_size = stk_classic::parallel_machine_size( pm );
434  const unsigned nLocalNode = nPerProc + ( 1 < p_size ? 1 : 0 );
435  const unsigned nLocalEdge = nPerProc ;
436 
437  std::vector<unsigned> local_count ;
438 
439  //------------------------------
440  {
441  bool aura = false;
442  RingFixture ring_mesh( pm , nPerProc , false /* no edge parts */ );
443  BulkData & bulk = ring_mesh.m_bulk_data;
444  ring_mesh.m_meta_data.commit();
445 
446  bulk.modification_begin();
447  ring_mesh.generate_mesh( );
448  STKUNIT_ASSERT(stk_classic::unit_test::modification_end_wrapper(bulk, aura));
449 
450  bulk.modification_begin();
451  ring_mesh.fixup_node_ownership( );
452  STKUNIT_ASSERT(stk_classic::unit_test::modification_end_wrapper(bulk, aura));
453 
454  const Selector select_used = ring_mesh.m_meta_data.locally_owned_part() |
455  ring_mesh.m_meta_data.globally_shared_part() ;
456  const Selector select_all = ring_mesh.m_meta_data.universal_part() ;
457 
458  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
459  STKUNIT_ASSERT_EQUAL( local_count[0] , nLocalNode );
460  STKUNIT_ASSERT_EQUAL( local_count[1] , nLocalEdge );
461 
462  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
463  STKUNIT_ASSERT_EQUAL( local_count[0] , nLocalNode );
464  STKUNIT_ASSERT_EQUAL( local_count[1] , nLocalEdge );
465 
466  if ( 1 < p_size ) {
467  // Shift ring by two nodes and edges.
468 
469  stk_classic::unit_test::test_shift_ring( ring_mesh, false /* no aura */ );
470 
471  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
472  STKUNIT_ASSERT( local_count[0] == nLocalNode );
473  STKUNIT_ASSERT( local_count[1] == nLocalEdge );
474 
475  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
476  STKUNIT_ASSERT( local_count[0] == nLocalNode );
477  STKUNIT_ASSERT( local_count[1] == nLocalEdge );
478  }
479  }
480 
481  //------------------------------
482  // Test shift starting with ghosting but not regenerated ghosting.
483  {
484  RingFixture ring_mesh( pm , nPerProc , false /* no edge parts */ );
485  BulkData& bulk = ring_mesh.m_bulk_data;
486  ring_mesh.m_meta_data.commit();
487 
488  bulk.modification_begin();
489  ring_mesh.generate_mesh( );
490  STKUNIT_ASSERT(bulk.modification_end());
491 
492  bulk.modification_begin();
493  ring_mesh.fixup_node_ownership( );
494  STKUNIT_ASSERT(bulk.modification_end());
495 
496  const Selector select_owned( ring_mesh.m_meta_data.locally_owned_part() );
497  const Selector select_used = ring_mesh.m_meta_data.locally_owned_part() |
498  ring_mesh.m_meta_data.globally_shared_part() ;
499  const Selector select_all( ring_mesh.m_meta_data.universal_part() );
500 
501  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
502  STKUNIT_ASSERT_EQUAL( local_count[0] , nLocalNode );
503  STKUNIT_ASSERT_EQUAL( local_count[1] , nLocalEdge );
504 
505  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
506  const unsigned n_extra = 1 < p_size ? 2 : 0 ;
507  STKUNIT_ASSERT( local_count[0] == nLocalNode + n_extra );
508  STKUNIT_ASSERT( local_count[1] == nLocalEdge + n_extra );
509 
510  if ( 1 < p_size ) {
511  stk_classic::unit_test::test_shift_ring( ring_mesh, false /* no aura */ );
512 
513  count_entities( select_owned , ring_mesh.m_bulk_data , local_count );
514  STKUNIT_ASSERT( local_count[0] == nPerProc );
515  STKUNIT_ASSERT( local_count[1] == nPerProc );
516 
517  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
518  STKUNIT_ASSERT( local_count[0] == nLocalNode );
519  STKUNIT_ASSERT( local_count[1] == nLocalEdge );
520 
521  // All of my ghosts were disrupted and therefore deleted:
522  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
523  STKUNIT_ASSERT_EQUAL( nLocalEdge , local_count[1] );
524  STKUNIT_ASSERT_EQUAL( nLocalNode , local_count[0] );
525  }
526  }
527  //------------------------------
528  // Test shift starting with ghosting and regenerating ghosting.
529  {
530  RingFixture ring_mesh( pm , nPerProc , false /* no edge parts */ );
531  BulkData& bulk = ring_mesh.m_bulk_data;
532  ring_mesh.m_meta_data.commit();
533 
534  bulk.modification_begin();
535  ring_mesh.generate_mesh( );
536  STKUNIT_ASSERT(bulk.modification_end());
537 
538  bulk.modification_begin();
539  ring_mesh.fixup_node_ownership( );
540  STKUNIT_ASSERT(bulk.modification_end());
541 
542  const Selector select_owned( ring_mesh.m_meta_data.locally_owned_part() );
543  const Selector select_used = ring_mesh.m_meta_data.locally_owned_part() |
544  ring_mesh.m_meta_data.globally_shared_part() ;
545  const Selector select_all( ring_mesh.m_meta_data.universal_part() );
546 
547  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
548  STKUNIT_ASSERT( local_count[0] == nLocalNode );
549  STKUNIT_ASSERT( local_count[1] == nLocalEdge );
550 
551  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
552  const unsigned n_extra = 1 < p_size ? 2 : 0 ;
553  STKUNIT_ASSERT( local_count[0] == nLocalNode + n_extra );
554  STKUNIT_ASSERT( local_count[1] == nLocalEdge + n_extra );
555 
556  if ( 1 < p_size ) {
557  stk_classic::unit_test::test_shift_ring( ring_mesh, true /* with aura */ );
558 
559  count_entities( select_owned , ring_mesh.m_bulk_data , local_count );
560  STKUNIT_ASSERT( local_count[0] == nPerProc );
561  STKUNIT_ASSERT( local_count[1] == nPerProc );
562 
563  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
564  STKUNIT_ASSERT( local_count[0] == nLocalNode );
565  STKUNIT_ASSERT( local_count[1] == nLocalEdge );
566 
567  // All of my ghosts were regenerated:
568  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
569  STKUNIT_ASSERT( local_count[0] == nLocalNode + n_extra );
570  STKUNIT_ASSERT( local_count[1] == nLocalEdge + n_extra );
571  }
572  }
573  //------------------------------
574  // Test bad owner change catching:
575  if ( 1 < p_size ) {
576  RingFixture ring_mesh( pm , nPerProc , false /* no edge parts */ );
577  BulkData& bulk = ring_mesh.m_bulk_data;
578  ring_mesh.m_meta_data.commit();
579 
580  bulk.modification_begin();
581  ring_mesh.generate_mesh( );
582  STKUNIT_ASSERT(bulk.modification_end());
583 
584  bulk.modification_begin();
585  ring_mesh.fixup_node_ownership( );
586  STKUNIT_ASSERT(bulk.modification_end());
587 
588  std::vector<EntityProc> change ;
589 
590  if ( 0 == p_rank ) {
591  change.resize(4);
592  // Error to change to bad owner:
593  change[0].first = ring_mesh.m_bulk_data.get_entity( 0 , ring_mesh.m_node_ids[1] );
594  change[0].second = p_size ;
595  // Error to change a ghost:
596  for ( std::vector<Entity*>::const_iterator
597  ec = ring_mesh.m_bulk_data.entity_comm().begin() ;
598  ec != ring_mesh.m_bulk_data.entity_comm().end() ; ++ec ) {
599  if ( in_receive_ghost( **ec ) ) {
600  change[1].first = *ec ;
601  break ;
602  }
603  }
604  change[1].second = p_rank ;
605  // Error to change to multiple owners:
606  change[2].first = ring_mesh.m_bulk_data.get_entity( 0 , ring_mesh.m_node_ids[1] );
607  change[2].second = ( p_rank + 1 ) % p_size ;
608  change[3].first = change[2].first ;
609  change[3].second = ( p_rank + 2 ) % p_size ;
610  }
611 
612  STKUNIT_ASSERT( ring_mesh.m_bulk_data.modification_begin() );
613 
614  STKUNIT_ASSERT_THROW( ring_mesh.m_bulk_data.change_entity_owner( change ),
615  std::runtime_error );
616  }
617  //------------------------------
618  // Test move one element with initial ghosting but not regenerated ghosting:
619  // last processor give its shared node to P0
620  if ( 1 < p_size ) {
621  RingFixture ring_mesh( pm , nPerProc , false /* no edge parts */ );
622  BulkData& bulk = ring_mesh.m_bulk_data;
623  ring_mesh.m_meta_data.commit();
624 
625  bulk.modification_begin();
626  ring_mesh.generate_mesh( );
627  STKUNIT_ASSERT(bulk.modification_end());
628 
629  bulk.modification_begin();
630  ring_mesh.fixup_node_ownership( );
631  STKUNIT_ASSERT(bulk.modification_end());
632 
633  const Selector select_owned( ring_mesh.m_meta_data.locally_owned_part() );
634  const Selector select_used = ring_mesh.m_meta_data.locally_owned_part() |
635  ring_mesh.m_meta_data.globally_shared_part() ;
636  const Selector select_all( ring_mesh.m_meta_data.universal_part() );
637 
638  std::vector<EntityProc> change ;
639 
640  if ( p_rank + 1 == p_size ) {
641  EntityProc entry ;
642  entry.first = ring_mesh.m_bulk_data.get_entity( 0 , ring_mesh.m_node_ids[0] );
643  entry.second = 0 ;
644  STKUNIT_ASSERT_EQUAL( p_rank , entry.first->owner_rank() );
645  change.push_back( entry );
646  }
647 
648  STKUNIT_ASSERT( ring_mesh.m_bulk_data.modification_begin() );
649  ring_mesh.m_bulk_data.change_entity_owner( change );
650  STKUNIT_ASSERT( stk_classic::unit_test::modification_end_wrapper( ring_mesh.m_bulk_data , false ) );
651 
652  count_entities( select_owned , ring_mesh.m_bulk_data , local_count );
653  const unsigned n_node = p_rank == 0 ? nPerProc + 1 : (
654  p_rank + 1 == p_size ? nPerProc - 1 :
655  nPerProc );
656 
657  STKUNIT_ASSERT_EQUAL( n_node , local_count[0] );
658  STKUNIT_ASSERT_EQUAL( (unsigned) nPerProc , local_count[1] );
659 
660  count_entities( select_used , ring_mesh.m_bulk_data , local_count );
661  STKUNIT_ASSERT_EQUAL( nLocalNode , local_count[0] );
662  STKUNIT_ASSERT_EQUAL( nLocalEdge , local_count[1] );
663 
664  // Moving the node disrupted ghosting on first and last process
665  count_entities( select_all , ring_mesh.m_bulk_data , local_count );
666  const unsigned n_extra = p_rank + 1 == p_size || p_rank == 0 ? 1 : 2 ;
667  STKUNIT_ASSERT_EQUAL( nLocalNode + n_extra , local_count[0] );
668  STKUNIT_ASSERT_EQUAL( nLocalEdge + n_extra , local_count[1] );
669  }
670 }
671 
672 //----------------------------------------------------------------------
673 //----------------------------------------------------------------------
674 // Testing for collection of boxes
675 
676 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeOwner_box)
677 {
678  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
679  MPI_Barrier( pm );
680 
681  const int root_box[3][2] = { { 0 , 4 } , { 0 , 5 } , { 0 , 6 } };
682 
683  const unsigned p_size = stk_classic::parallel_machine_size( pm );
684 
685  const int spatial_dimension = 3;
686  MetaData meta( stk_classic::mesh::fem::entity_rank_names(spatial_dimension) );
687 
688  meta.commit();
689 
690  //------------------------------
691  {
692  bool aura = false;
693  BoxFixture fixture( pm, 100 );
694  fixture.fem_meta().commit();
695  BulkData & bulk = fixture.bulk_data();
696  int local_box[3][2] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } };
697 
698  bulk.modification_begin();
699  fixture.generate_boxes( root_box, local_box );
700  STKUNIT_ASSERT(stk_classic::unit_test::modification_end_wrapper(bulk, aura));
701 
702  if ( 1 < p_size ) {
703  donate_one_element( bulk , aura );
704  }
705  }
706 
707  if ( 1 < p_size ) {
708  bool aura = false;
709  BoxFixture fixture( pm, 100 );
710  fixture.fem_meta().commit();
711  BulkData & bulk = fixture.bulk_data();
712  int local_box[3][2] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } };
713 
714  bulk.modification_begin();
715  fixture.generate_boxes( root_box, local_box );
716  STKUNIT_ASSERT(stk_classic::unit_test::modification_end_wrapper(bulk, aura));
717 
718  donate_all_shared_nodes( bulk , aura );
719  }
720  //------------------------------
721  if ( 1 < p_size ) {
722  bool aura = false;
723  BoxFixture fixture( pm, 100 );
724  fixture.fem_meta().commit();
725  BulkData & bulk = fixture.bulk_data();
726  int local_box[3][2] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } };
727 
728  bulk.modification_begin();
729  fixture.generate_boxes( root_box, local_box );
730  STKUNIT_ASSERT(stk_classic::unit_test::modification_end_wrapper(bulk, aura));
731 
732  donate_one_element( bulk , false /* no aura */ );
733  }
734  //------------------------------
735  // Introduce ghosts:
736  if ( 1 < p_size ) {
737  BoxFixture fixture( pm, 100 );
738  BulkData & bulk = fixture.bulk_data();
739  FEMMetaData & box_meta = fixture.fem_meta();
740  box_meta.commit();
741  int local_box[3][2] = { { 0 , 0 } , { 0 , 0 } , { 0 , 0 } };
742 
743  bulk.modification_begin();
744  fixture.generate_boxes( root_box, local_box );
745  STKUNIT_ASSERT(bulk.modification_end());
746 
747  std::vector<unsigned> used_count ;
748  std::vector<unsigned> all_count ;
749 
750  const Selector select_owned( box_meta.locally_owned_part() );
751  const Selector select_used = box_meta.locally_owned_part() |
752  box_meta.globally_shared_part() ;
753  const Selector select_all( box_meta.universal_part() );
754 
755  count_entities( select_all , bulk , all_count );
756  count_entities( select_used , bulk , used_count );
757 
758  STKUNIT_ASSERT( used_count[0] < all_count[0] );
759  STKUNIT_ASSERT( used_count[3] < all_count[3] );
760 
761  donate_all_shared_nodes( bulk , false /* don't regenerate aura */ );
762 
763  count_entities( select_all , bulk , all_count );
764  count_entities( select_used , bulk , used_count );
765 
766  STKUNIT_ASSERT_EQUAL( used_count[0] , all_count[0] );
767  STKUNIT_ASSERT_EQUAL( used_count[3] , all_count[3] );
768  }
769 }
770 
771 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testModifyPropagation)
772 {
773  // Our new modification model makes it so the modified status
774  // of an entity is propagated up to higher-ranked entities
775  // that have relations to the modified entity. We test this
776  // by grabbing a node off of a ring mesh, modifying it, and
777  // checking that its edge also gets marked as modified.
778 
779  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
780  MPI_Barrier( pm );
781 
782  const unsigned nPerProc = 2;
783  const unsigned p_size = stk_classic::parallel_machine_size( pm );
784 
785  // this test only needs to be run w/ one processor
786  if (p_size > 1) return;
787 
788  // Make a ring_mesh and add an extra part
789  RingFixture ring_mesh( pm , nPerProc, false /* don't use edge parts */);
790  stk_classic::mesh::Part& special_part =
791  ring_mesh.m_meta_data.declare_part("special_node_part", stk_classic::mesh::BaseEntityRank );
792  ring_mesh.m_meta_data.commit();
793  BulkData& bulk = ring_mesh.m_bulk_data;
794 
795  bulk.modification_begin();
796  ring_mesh.generate_mesh( );
797  STKUNIT_ASSERT(bulk.modification_end());
798 
799  bulk.modification_begin();
800  ring_mesh.fixup_node_ownership( );
801  STKUNIT_ASSERT(bulk.modification_end());
802 
803  // grab the first edge
804  EntityVector edges;
805  const stk_classic::mesh::EntityRank element_rank = ring_mesh.m_meta_data.element_rank();
806  stk_classic::mesh::get_entities( ring_mesh.m_bulk_data, element_rank, edges );
807  stk_classic::mesh::Entity& edge = *( edges.front() );
808 
809  // get one of the nodes related to this edge
810  PairIterRelation node_relations = edge.relations( stk_classic::mesh::BaseEntityRank );
811  STKUNIT_ASSERT( !node_relations.empty() );
812  stk_classic::mesh::Entity& node = *( node_relations.front().entity());
813  STKUNIT_ASSERT_EQUAL( node.entity_rank(), (unsigned) stk_classic::mesh::BaseEntityRank );
814 
815  // make a modification to the node by changing its parts
816  ring_mesh.m_bulk_data.modification_begin();
818  parts.push_back( &special_part );
819  ring_mesh.m_bulk_data.change_entity_parts( node, parts );
820 
821  // check that the node AND it's edge are marked as modified
822  STKUNIT_ASSERT_EQUAL ( node.log_query(), stk_classic::mesh::EntityLogModified );
823  STKUNIT_ASSERT_EQUAL ( edge.log_query(), stk_classic::mesh::EntityLogModified );
824 
825  STKUNIT_ASSERT ( ring_mesh.m_bulk_data.modification_end() );
826 }
827 
828 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeEntityOwnerFromSelfToSelf)
829 {
830  // It should be legal to "change" entity ownership from yourself to yourself.
831  //
832  // 1---3---5
833  // | 1 | 2 |
834  // 2---4---6
835  //
836  // To test this, we use the mesh above, with elem 1 going on rank 0 and
837  // elem 2 going on rank 1. Nodes 3,4 are shared. After the mesh is set up
838  // we change the ownership of a few nodes to the same proc that already
839  // owns them.
840 
841  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
842 
843  // Set up meta and bulk data
844  const unsigned spatial_dim = 2;
845  FEMMetaData meta_data(spatial_dim);
846  meta_data.commit();
847  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
848  unsigned p_rank = mesh.parallel_rank();
849  unsigned p_size = mesh.parallel_size();
850 
851  // Bail if we only have one proc
852  if (p_size == 1) {
853  return;
854  }
855 
856  // Begin modification cycle so we can create the entities and relations
857  mesh.modification_begin();
858 
859  EntityVector nodes;
860  const unsigned nodes_per_elem = 4, nodes_per_side = 2;
861 
862  if (p_rank < 2) {
863  // We're just going to add everything to the universal part
864  stk_classic::mesh::PartVector empty_parts;
865 
866  // Create element
867  const EntityRank elem_rank = meta_data.element_rank();
868  Entity & elem = mesh.declare_entity(elem_rank,
869  p_rank+1, //elem_id
870  empty_parts);
871 
872  // Create nodes
873  const unsigned starting_node_id = p_rank * nodes_per_side + 1;
874  for (unsigned id = starting_node_id; id < starting_node_id + nodes_per_elem; ++id) {
875  nodes.push_back(&mesh.declare_entity(NODE_RANK,
876  id,
877  empty_parts));
878  }
879 
880  // Add relations to nodes
881  unsigned rel_id = 0;
882  for (EntityVector::iterator itr = nodes.begin(); itr != nodes.end(); ++itr, ++rel_id) {
883  mesh.declare_relation( elem, **itr, rel_id );
884  }
885  }
886 
887  mesh.modification_end();
888 
889  mesh.modification_begin();
890 
891  std::vector<EntityProc> change ;
892  if (p_rank < 2) {
893  // Change ownership of some nodes to the same proc that owns them
894 
895  // Add a non-shared node to change list
896  if ( p_rank == 0 ) {
897  EntityProc entry( nodes.front(), p_rank ) ;
898  change.push_back( entry );
899  }
900  else {
901  EntityProc entry( nodes.back(), p_rank ) ;
902  change.push_back( entry );
903  }
904 
905  // Add a shared node to change list
906  Entity* shared_node = nodes[p_rank == 0 ? nodes_per_side : 0];
907  EntityId expected_id = 3;
908  Part& shared_part = meta_data.globally_shared_part();
909  STKUNIT_ASSERT( has_superset(shared_node->bucket(), shared_part) );
910  STKUNIT_ASSERT_EQUAL(shared_node->identifier(), expected_id);
911  if (shared_node->owner_rank() == p_rank) {
912  EntityProc entry( shared_node, p_rank );
913  change.push_back( entry );
914  }
915  }
916 
917  mesh.change_entity_owner(change);
918 
919  mesh.modification_end();
920 }
921 
922 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeEntityOwnerOfShared)
923 {
924  // This unit-test is designed to test the conditions that results that
925  // resulted in the difficult-to-fix rebalance use-case bug. Specifically,
926  // it will test the changing-of-ownership of a shared edge to a proc that
927  // either ghosted it or did not know about it.
928  //
929  // 1---3---5---7
930  // | 1 | 2 | 3 | ...
931  // 2---4---6---8
932  //
933  // To test this, we use the mesh above, with each elem going on a separate
934  // proc, one elem per proc. We will take the edge shared by the last
935  // two (rightmost) elements and change the ownership to proc 0.
936 
937  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
938 
939  // Set up meta and bulk data
940  const unsigned spatial_dim = 2;
941  FEMMetaData meta_data(spatial_dim);
942  meta_data.commit();
943  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
944  unsigned p_rank = mesh.parallel_rank();
945  unsigned p_size = mesh.parallel_size();
946  const EntityRank edge_rank = meta_data.edge_rank();
947  const EntityRank elem_rank = meta_data.element_rank();
948 
949  // Bail if we have fewer than 3 procs
950  if (p_size < 3) {
951  return;
952  }
953 
954  // Begin modification cycle so we can create the entities and relations
955  mesh.modification_begin();
956 
957  const unsigned nodes_per_elem = 4, nodes_per_side = 2;
958  EntityKey elem_key_chg_own(elem_rank, p_size - 1 /*id*/);
959  EntityKey edge_key_chg_own(edge_rank, 1 /*id*/);
960 
961  // We're just going to add everything to the universal part
962  stk_classic::mesh::PartVector empty_parts;
963 
964  // Create element
965  Entity & elem = mesh.declare_entity(elem_rank,
966  p_rank+1, //elem_id
967  empty_parts);
968 
969  // If it is 2nd to last element, it is the one changing
970  if (p_rank == p_size - 2) {
971  STKUNIT_ASSERT(elem_key_chg_own == elem.key());
972  }
973 
974  // Create nodes
975  EntityVector nodes;
976  const unsigned starting_node_id = p_rank * nodes_per_side + 1;
977  for (unsigned id = starting_node_id; id < starting_node_id + nodes_per_elem; ++id) {
978  nodes.push_back(&mesh.declare_entity(NODE_RANK,
979  id,
980  empty_parts));
981  }
982 
983  // Add relations to nodes
984  unsigned rel_id = 0;
985  for (EntityVector::iterator itr = nodes.begin(); itr != nodes.end(); ++itr, ++rel_id) {
986  mesh.declare_relation( elem, **itr, rel_id );
987  }
988 
989  // Create edge on last two procs
990 
991  if (p_rank >= p_size - 2) {
992  Entity& edge = mesh.declare_entity(edge_rank,
993  1, // id
994  empty_parts);
995  STKUNIT_ASSERT(edge.key() == edge_key_chg_own);
996 
997  // Add relation from elem to edge
998  mesh.declare_relation( elem, edge, 1 /*rel-id*/);
999 
1000  // Add relations from edge to nodes
1001  unsigned start_idx = p_rank == p_size - 1 ? 0 : nodes_per_side;
1002  unsigned end_idx = start_idx + nodes_per_side;
1003  rel_id = 0;
1004  for (unsigned idx = start_idx ;
1005  start_idx < end_idx;
1006  ++start_idx, ++rel_id) {
1007  mesh.declare_relation( edge, *nodes[idx], rel_id );
1008  }
1009  }
1010 
1011  mesh.modification_end();
1012 
1013  // Changing elem and edge should be ghosted or unknown on proc 0
1014  if (p_rank == 0) {
1015  // Get the two entities
1016  Entity* changing_elem = mesh.get_entity(elem_key_chg_own);
1017  Entity* changing_edge = mesh.get_entity(edge_key_chg_own);
1018  if (p_size == 3) {
1019  // Should be ghosted
1020  STKUNIT_ASSERT(changing_elem != NULL);
1021  STKUNIT_ASSERT(changing_edge != NULL);
1022 
1023  // Verify that the entities are ghosted
1024  Part& owned = meta_data.locally_owned_part();
1025  Part& shared = meta_data.globally_shared_part();
1026  STKUNIT_ASSERT(!(changing_elem->bucket().member(owned) ||
1027  changing_elem->bucket().member(shared)));
1028  STKUNIT_ASSERT(!(changing_edge->bucket().member(owned) ||
1029  changing_edge->bucket().member(shared)));
1030  }
1031  else {
1032  // Should be NULL
1033  STKUNIT_ASSERT(changing_elem == NULL);
1034  STKUNIT_ASSERT(changing_edge == NULL);
1035  }
1036  }
1037 
1038  mesh.modification_begin();
1039 
1040  std::vector<EntityProc> change ;
1041  if (p_rank >= p_size - 2) {
1042  // Change ownership of changing elem and all entities in it's closure that
1043  // we own to proc 0.
1044 
1045  Entity* changing_elem = mesh.get_entity(elem_key_chg_own);
1046  if (p_rank == p_size - 2) {
1047  EntityProc eproc(changing_elem, 0 /*new owner*/);
1048  change.push_back(eproc);
1049  }
1050 
1051  for (PairIterRelation i = changing_elem->relations() ; !i.empty() ; ++i) {
1052  if (i->entity()->owner_rank() == p_rank) {
1053  EntityProc eproc(i->entity(), 0 /*new owner*/);
1054  change.push_back(eproc);
1055  }
1056  }
1057  }
1058 
1059  mesh.change_entity_owner(change);
1060 
1061  mesh.modification_end();
1062 
1063  // Changing elem and edge should now be owned by proc 0
1064  if (p_rank == 0) {
1065  // Get the two ghosted entities, check that they were found
1066  Entity* changing_elem = mesh.get_entity(elem_key_chg_own);
1067  Entity* changing_edge = mesh.get_entity(edge_key_chg_own);
1068  STKUNIT_ASSERT(changing_elem != NULL);
1069  STKUNIT_ASSERT(changing_edge != NULL);
1070 
1071  // Verify that the entities are ghosted
1072  Part& owned = meta_data.locally_owned_part();
1073  STKUNIT_ASSERT( changing_elem->bucket().member(owned) );
1074  STKUNIT_ASSERT( changing_edge->bucket().member(owned) );
1075  }
1076 }
1077 
1078 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testFamilyTreeGhosting)
1079 {
1080  // A family tree is a higher-rank entity (rank = element_rank() + 1) that
1081  // has down-relations to elements used, for example, to hold parent/child
1082  // relations in an adapted mesh.
1083  //
1084  // 1---3---5---7
1085  // | 1 | 2 | 3 | ...
1086  // 2---4---6---8
1087  //
1088  // To test this, we use the mesh above, with each elem going on a separate
1089  // proc, one elem per proc.
1090  // After the mesh is set up we add rank-3 (family tree) entities and have them point down to
1091  // just the single rank-2 elements. Then we check that they are properly
1092  // ghosted after modification_end.
1093 
1094  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
1095 
1096  // Set up meta and bulk data
1097  const unsigned spatial_dim = 2;
1098 
1099  std::vector<std::string> entity_rank_names = stk_classic::mesh::fem::entity_rank_names(spatial_dim);
1100  entity_rank_names.push_back("FAMILY_TREE");
1101 
1102  FEMMetaData meta_data(spatial_dim, entity_rank_names);
1103  meta_data.commit();
1104  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
1105  unsigned p_rank = mesh.parallel_rank();
1106  unsigned p_size = mesh.parallel_size();
1107 
1108  Part& owned = meta_data.locally_owned_part();
1109  Part& shared = meta_data.globally_shared_part();
1110 
1111  //
1112  // Begin modification cycle so we can create the entities and relations
1113  //
1114 
1115  mesh.modification_begin();
1116 
1117  EntityVector nodes;
1118  const unsigned nodes_per_elem = 4, nodes_per_side = 2;
1119  const EntityRank family_tree_rank = meta_data.element_rank() + 1;
1120  const EntityId my_family_tree_id = p_rank+1;
1121 
1122  // We're just going to add everything to the universal part
1123  stk_classic::mesh::PartVector empty_parts;
1124 
1125  // Create element
1126  const EntityRank elem_rank = meta_data.element_rank();
1127  Entity & elem = mesh.declare_entity(elem_rank,
1128  p_rank+1, //elem_id
1129  empty_parts);
1130 
1131  // Create nodes
1132  const unsigned starting_node_id = p_rank * nodes_per_side + 1;
1133  for (unsigned id = starting_node_id; id < starting_node_id + nodes_per_elem; ++id) {
1134  nodes.push_back(&mesh.declare_entity(NODE_RANK,
1135  id,
1136  empty_parts));
1137  }
1138 
1139  // Add relations to nodes
1140  unsigned rel_id = 0;
1141  for (EntityVector::iterator itr = nodes.begin(); itr != nodes.end(); ++itr, ++rel_id) {
1142  mesh.declare_relation( elem, **itr, rel_id );
1143  }
1144 
1145  // Create family tree
1146  Entity & family_tree = mesh.declare_entity(family_tree_rank,
1147  my_family_tree_id,
1148  empty_parts);
1149  // Add relation to element
1150  unsigned downward_ordinal = 0; // we only have 1 down relation, it has ordinal 0
1151  mesh.declare_relation( family_tree, elem, downward_ordinal);
1152 
1153  mesh.modification_end();
1154 
1155  //
1156  // Test correctness of ghosting: Check that adjacent family-trees are ghosted on this proc
1157  //
1158 
1159  // Compute and store ids of adjacent family-trees
1160  std::vector<EntityId> family_tree_ghost_ids;
1161  if (p_rank > 0) {
1162  family_tree_ghost_ids.push_back(my_family_tree_id - 1);
1163  }
1164  if (p_rank < p_size - 1) {
1165  family_tree_ghost_ids.push_back(my_family_tree_id + 1);
1166  }
1167 
1168  // Check that my_family_tree exists and I own it
1169  Entity *my_family_tree = mesh.get_entity(family_tree_rank, my_family_tree_id);
1170  STKUNIT_ASSERT(my_family_tree);
1171  STKUNIT_ASSERT( (p_rank) == my_family_tree->owner_rank());
1172 
1173  // Check that adjacent family-trees exist and are ghosted
1174  for (std::vector<EntityId>::const_iterator
1175  itr = family_tree_ghost_ids.begin(); itr != family_tree_ghost_ids.end(); ++itr) {
1176  EntityId expected_ghosted_family_tree_id = *itr;
1177 
1178  Entity *expected_ghosted_family_tree = mesh.get_entity(family_tree_rank, expected_ghosted_family_tree_id);
1179  STKUNIT_ASSERT(expected_ghosted_family_tree);
1180  STKUNIT_ASSERT(expected_ghosted_family_tree_id - 1 == expected_ghosted_family_tree->owner_rank());
1181 
1182  stk_classic::mesh::Bucket& bucket = expected_ghosted_family_tree->bucket();
1183  STKUNIT_ASSERT(!bucket.member(owned) && !bucket.member(shared));
1184  }
1185 }
1186 
1187 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, test_other_ghosting)
1188 {
1189  //
1190  // 1---3---5---7
1191  // | 1 | 2 | 3 | ...
1192  // 2---4---6---8
1193  //
1194  // To test this, we use the mesh above, with each elem going on a separate
1195  // proc, one elem per proc.
1196 
1197  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
1198 
1199  // Set up meta and bulk data
1200  const unsigned spatial_dim = 2;
1201 
1202  std::vector<std::string> entity_rank_names = stk_classic::mesh::fem::entity_rank_names(spatial_dim);
1203  //entity_rank_names.push_back("FAMILY_TREE");
1204 
1205  FEMMetaData meta_data(spatial_dim, entity_rank_names);
1206  meta_data.commit();
1207  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
1208  unsigned p_rank = mesh.parallel_rank();
1209  unsigned p_size = mesh.parallel_size();
1210 
1211  if (p_size != 3) return;
1212 
1213  //Part& owned = meta_data.locally_owned_part();
1214  //Part& shared = meta_data.globally_shared_part();
1215 
1216  //
1217  // Begin modification cycle so we can create the entities and relations
1218  //
1219 
1220  mesh.modification_begin();
1221 
1222  EntityVector nodes;
1223  const unsigned nodes_per_elem = 4, nodes_per_side = 2;
1224 
1225  // We're just going to add everything to the universal part
1226  stk_classic::mesh::PartVector empty_parts;
1227 
1228  // Create element
1229  const EntityRank elem_rank = meta_data.element_rank();
1230  Entity & elem = mesh.declare_entity(elem_rank,
1231  p_rank+1, //elem_id
1232  empty_parts);
1233 
1234  // Create nodes
1235  const unsigned starting_node_id = p_rank * nodes_per_side + 1;
1236  for (unsigned id = starting_node_id; id < starting_node_id + nodes_per_elem; ++id) {
1237  nodes.push_back(&mesh.declare_entity(NODE_RANK,
1238  id,
1239  empty_parts));
1240  std::cout << "P[" << p_rank << "] node id= " << id << std::endl;
1241  }
1242 
1243  // Add relations to nodes
1244  unsigned rel_id = 0;
1245  for (EntityVector::iterator itr = nodes.begin(); itr != nodes.end(); ++itr, ++rel_id) {
1246  mesh.declare_relation( elem, **itr, rel_id );
1247  }
1248 
1249  if (1 && p_rank == 2)
1250  {
1251  Entity& node = mesh.declare_entity(NODE_RANK,
1252  4,
1253  empty_parts);
1254  //Entity* node = mesh.get_entity(NODE_RANK,
1255  //4);
1256 
1257  mesh.declare_relation(elem, node, 4);
1258  }
1259 
1260  mesh.modification_end();
1261 
1262  if (p_rank == 0)
1263  {
1264  unsigned id=4;
1265  Entity *node = mesh.get_entity(0, id);
1266  std::cout << "P[" << p_rank << "] node " << node << " own= " << node->owner_rank() << std::endl;
1267 
1268  {
1269  PairIterRelation rels = node->relations();
1270  for (unsigned i = 0; i < rels.size(); i++)
1271  {
1272  std::cout << "P[" << p_rank << "] rel = " << rels[i].entity()->owner_rank() << std::endl;
1273  }
1274  }
1275  }
1276 
1277  mesh.modification_begin();
1278  if (p_rank == 1)
1279  {
1280  Entity *this_elem = mesh.get_entity(2, 2);
1281  if (!mesh.destroy_entity(this_elem)) exit(2);
1282  }
1283  if (p_rank == 2)
1284  {
1285  Entity *this_elem = mesh.get_entity(2, 3);
1286  if (!mesh.destroy_entity(this_elem)) exit(2);
1287  }
1288  mesh.modification_end();
1289 
1290  if (1 || p_rank == 2)
1291  {
1292  unsigned id=4;
1293  Entity *node = mesh.get_entity(0, id);
1294  //
1295 
1296  {
1297  PairIterRelation rels = node->relations();
1298  for (unsigned i = 0; i < rels.size(); i++)
1299  {
1300  std::cout << "P[" << p_rank << "] node " << node << " own= " << node->owner_rank() << " rel = " << rels[i].entity()->owner_rank() << std::endl;
1301  }
1302  }
1303  }
1304 
1305  //exit(123);
1306 
1307 #if 0
1308  //
1309  // Test correctness of ghosting: Check that adjacent family-trees are ghosted on this proc
1310  //
1311 
1312  // Compute and store ids of adjacent family-trees
1313  std::vector<EntityId> family_tree_ghost_ids;
1314  if (p_rank > 0) {
1315  family_tree_ghost_ids.push_back(my_family_tree_id - 1);
1316  }
1317  if (p_rank < p_size - 1) {
1318  family_tree_ghost_ids.push_back(my_family_tree_id + 1);
1319  }
1320 
1321  // Check that my_family_tree exists and I own it
1322  Entity *my_family_tree = mesh.get_entity(family_tree_rank, my_family_tree_id);
1323  STKUNIT_ASSERT(my_family_tree);
1324  STKUNIT_ASSERT( (p_rank) == my_family_tree->owner_rank());
1325 
1326  // Check that adjacent family-trees exist and are ghosted
1327  for (std::vector<EntityId>::const_iterator
1328  itr = family_tree_ghost_ids.begin(); itr != family_tree_ghost_ids.end(); ++itr) {
1329  EntityId expected_ghosted_family_tree_id = *itr;
1330 
1331  Entity *expected_ghosted_family_tree = mesh.get_entity(family_tree_rank, expected_ghosted_family_tree_id);
1332  STKUNIT_ASSERT(expected_ghosted_family_tree);
1333  STKUNIT_ASSERT(expected_ghosted_family_tree_id - 1 == expected_ghosted_family_tree->owner_rank());
1334 
1335  stk_classic::mesh::Bucket& bucket = expected_ghosted_family_tree->bucket();
1336  STKUNIT_ASSERT(!bucket.member(owned) && !bucket.member(shared));
1337  }
1338 #endif
1339 }
1340 
1341 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, testChangeEntityPartsOfShared)
1342 {
1343  //
1344  // This unit-test is designed to test what happens when a shared entity
1345  // is moved on one processor during the same modification cycle in which
1346  // it was declared.
1347  //
1348  // 1---3---5
1349  // | 1 | 2 |
1350  // 2---4---6
1351  //
1352  // To test this, we use the mesh above, with each elem going on a separate
1353  // proc, one elem per proc. Node 3 is the node we'll be testing.
1354  //
1355 
1356  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
1357 
1358  // Set up meta and bulk data
1359  const unsigned spatial_dim = 2;
1360  FEMMetaData meta_data(spatial_dim);
1361  const EntityRank node_rank = meta_data.node_rank();
1362  const EntityRank elem_rank = meta_data.element_rank();
1363 
1364  stk_classic::mesh::Part& extra_node_part = meta_data.declare_part("extra_node_part", node_rank);
1365  meta_data.commit();
1366 
1367  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
1368  unsigned p_rank = mesh.parallel_rank();
1369  unsigned p_size = mesh.parallel_size();
1370 
1371  // Bail unless in parallel
1372  if (p_size == 1) {
1373  return;
1374  }
1375 
1376  // Begin modification cycle so we can create the entities and relations
1377  if (p_rank < 2) {
1378  mesh.modification_begin();
1379 
1380  const unsigned nodes_per_elem = 4, nodes_per_side = 2;
1381  EntityKey node_key_to_move(node_rank, 3 /*id*/);
1382 
1383  // We're just going to add everything to the universal part
1384  stk_classic::mesh::PartVector empty_parts;
1385 
1386  // Create element
1387  Entity & elem = mesh.declare_entity(elem_rank,
1388  p_rank+1, //elem_id
1389  empty_parts);
1390 
1391  // Create nodes
1392  EntityVector nodes;
1393  const unsigned starting_node_id = p_rank * nodes_per_side + 1;
1394  for (unsigned id = starting_node_id; id < starting_node_id + nodes_per_elem; ++id) {
1395  nodes.push_back(&mesh.declare_entity(NODE_RANK,
1396  id,
1397  empty_parts));
1398  }
1399 
1400  // Add relations to nodes
1401  unsigned rel_id = 0;
1402  for (EntityVector::iterator itr = nodes.begin(); itr != nodes.end(); ++itr, ++rel_id) {
1403  mesh.declare_relation( elem, **itr, rel_id );
1404  }
1405 
1406  // On the processor that does *not* end up as the owner of the node, change its parts
1407  Entity& changing_node = *mesh.get_entity(node_key_to_move);
1408  if (p_rank == 0) {
1409  PartVector add_parts(1, &extra_node_part);
1410  mesh.change_entity_parts(changing_node, add_parts);
1411  }
1412 
1413  mesh.modification_end();
1414 
1415  // Expect that this is a shared node
1416  STKUNIT_EXPECT_FALSE(changing_node.sharing().empty());
1417 
1418  // Expect that part change had no impact since it was on the proc that did not end
1419  // up as the owner
1420  STKUNIT_EXPECT_FALSE(changing_node.bucket().member(extra_node_part));
1421 
1422  mesh.modification_begin();
1423 
1424  // On the processor that owns the node, change its parts
1425  if (p_rank == 1) {
1426  PartVector add_parts(1, &extra_node_part);
1427  mesh.change_entity_parts(changing_node, add_parts);
1428  }
1429 
1430  mesh.modification_end();
1431 
1432  // Expect that the part change *did* have an impact
1433  STKUNIT_EXPECT_TRUE(changing_node.bucket().member(extra_node_part));
1434  }
1435  else {
1436  // On extra procs, do bare minimum
1437  mesh.modification_begin();
1438  mesh.modification_end();
1439  mesh.modification_begin();
1440  mesh.modification_end();
1441  }
1442 }
1443 
1444 STKUNIT_UNIT_TEST(UnitTestingOfBulkData, test_final_modification_end)
1445 {
1446  stk_classic::ParallelMachine pm = MPI_COMM_WORLD;
1447 
1448  const unsigned spatial_dim = 2;
1449  FEMMetaData meta_data(spatial_dim);
1450  meta_data.commit();
1451 
1452  BulkData mesh(FEMMetaData::get_meta_data(meta_data), pm);
1453 
1454  mesh.modification_begin();
1455  mesh.final_modification_end();
1456 
1457  STKUNIT_ASSERT_THROW(mesh.modification_begin(), std::logic_error );
1458 
1459 }
Part & locally_owned_part() const
Subset for the problem domain that is owned by the local process. Ghost entities are not members of t...
void declare_relation(Entity &e_from, Entity &e_to, const RelationIdentifier local_id)
Declare a relation and its converse between entities in the same mesh.
FEMMetaData is a class that implements a Finite Element Method skin on top of the Sierra Tool Kit Met...
Definition: FEMMetaData.hpp:54
bool has_superset(const Bucket &bucket, const unsigned &ordinal)
Is this bucket a subset of the given part by partID.
Definition: Bucket.cpp:127
The manager of an integrated collection of parts and fields.
Definition: MetaData.hpp:56
PairIterEntityComm sharing() const
Parallel processes which share this entity.
Definition: Entity.hpp:178
const std::vector< Entity * > & entity_comm() const
All entities with communication information.
Definition: BulkData.hpp:367
Part & universal_part() const
Universal subset for the problem domain. All other parts are a subset of the universal part...
Bucket & bucket() const
The bucket which holds this mesh entity&#39;s field data.
Definition: Entity.hpp:141
This is a class for selecting buckets based on a set of meshparts and set logic.
Definition: Selector.hpp:112
const EntityKey & key() const
The globally unique key ( entity type + identifier ) of this entity.
Definition: Entity.hpp:138
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...
std::pair< Entity *, unsigned > EntityProc
Pairing of an entity with a processor rank.
Definition: Types.hpp:111
EntityModificationLog log_query() const
Query the current state of the entity log.
Definition: Entity.hpp:125
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
void change_entity_parts(Entity &entity, const PartVector &add_parts, const PartVector &remove_parts=PartVector())
Change the parallel-locally-owned entity&#39;s part membership by adding and/or removing parts...
Definition: BulkData.hpp:249
bool modification_end()
Parallel synchronization of modifications and transition to the guaranteed parallel consistent state...
unsigned parallel_size() const
Size of the parallel machine.
Definition: BulkData.hpp:82
Part & globally_shared_part() const
Subset for the problem domain that is shared with another process. Ghost entities are not members of ...
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
PairIterRelation relations() const
All Entity relations for which this entity is a member. The relations are ordered from lowest entity-...
Definition: Entity.hpp:161
void get_entities(const BulkData &mesh, EntityRank entity_rank, std::vector< Entity *> &entities)
Get all entities of the specified type, sorted by ID.
Definition: GetEntities.cpp:25
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
void commit()
Commit the part and field declarations so that the meta data manager can be used to create mesh bulk ...
A fundamental unit within the discretization of a problem domain, including but not limited to nodes...
Definition: Entity.hpp:120
void count_entities(const Selector &selector, const BulkData &mesh, std::vector< EntityRank > &count)
Local count selected entities of each type.
MPI_Comm ParallelMachine
Definition: Parallel.hpp:32
void change_entity_owner(const std::vector< EntityProc > &arg_change)
Give away ownership of entities to other parallel processes.
size_t capacity() const
Capacity of this bucket.
Definition: Bucket.hpp:122
unsigned parallel_rank() const
Rank of the parallel machine&#39;s local processor.
Definition: BulkData.hpp:85
Entity & declare_entity(EntityRank ent_rank, EntityId ent_id, const PartVector &parts)
Create or retrieve a locally owned entity of a given rank and id.
Definition: BulkData.cpp:215
EntityId identifier() const
Identifier for this entity which is globally unique for a given entity type.
Definition: Entity.hpp:133
std::vector< Part *> PartVector
Collections of parts are frequently maintained as a vector of Part pointers.
Definition: Types.hpp:31
A container for the field data of a homogeneous collection of entities.
Definition: Bucket.hpp:94
bool destroy_entity(Entity *&entity)
Request the destruction an entity on the local process.
Definition: BulkData.cpp:698
bool member(const Part &) const
Bucket is a subset of the given part.
Definition: Bucket.cpp:60
unsigned owner_rank() const
Parallel processor rank of the processor which owns this entity.
Definition: Entity.hpp:175