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 }
stk_classic::mesh::Entity::sharing
PairIterEntityComm sharing() const
Parallel processes which share this entity.
Definition: Entity.hpp:178
stk_classic::mesh::EntityKey
Integer type for the entity keys, which is an encoding of the entity type and entity identifier.
Definition: base/EntityKey.hpp:63
stk_classic::mesh::BulkData::change_entity_owner
void change_entity_owner(const std::vector< EntityProc > &arg_change)
Give away ownership of entities to other parallel processes.
Definition: BulkDataOwner.cpp:313
stk_classic::mesh::fixtures::RingFixture
Definition: RingFixture.hpp:37
stk_classic::mesh::Bucket
A container for the field data of a homogeneous collection of entities.
Definition: Bucket.hpp:94
stk_classic::mesh::BulkData::parallel_size
unsigned parallel_size() const
Size of the parallel machine.
Definition: BulkData.hpp:82
stk_classic::parallel_machine_size
unsigned parallel_machine_size(ParallelMachine parallel_machine)
Member function parallel_machine_size ...
Definition: Parallel.cpp:18
stk_classic::mesh::BulkData::parallel_rank
unsigned parallel_rank() const
Rank of the parallel machine's local processor.
Definition: BulkData.hpp:85
stk_classic::mesh::fixtures::BoxFixture
Definition: BoxFixture.hpp:26
stk_classic::mesh::Entity::key
const EntityKey & key() const
The globally unique key ( entity type + identifier ) of this entity.
Definition: Entity.hpp:138
stk_classic::mesh::BulkData::destroy_entity
bool destroy_entity(Entity *&entity)
Request the destruction an entity on the local process.
Definition: BulkData.cpp:698
stk_classic::mesh::count_entities
void count_entities(const Selector &selector, const BulkData &mesh, std::vector< EntityRank > &count)
Local count selected entities of each type.
Definition: GetEntities.cpp:113
stk_classic::mesh::Bucket::member
bool member(const Part &) const
Bucket is a subset of the given part.
Definition: Bucket.cpp:60
stk_classic::mesh::Part
An application-defined subset of a problem domain.
Definition: Part.hpp:49
stk_classic::mesh::BulkData::declare_entity
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
stk_classic::mesh::BulkData::change_entity_parts
void change_entity_parts(Entity &entity, const PartVector &add_parts, const PartVector &remove_parts=PartVector())
Change the parallel-locally-owned entity's part membership by adding and/or removing parts.
Definition: BulkData.hpp:249
stk_classic::mesh::fem::FEMMetaData::locally_owned_part
Part & locally_owned_part() const
Subset for the problem domain that is owned by the local process. Ghost entities are not members of t...
Definition: FEMMetaData.hpp:277
stk_classic::ParallelMachine
MPI_Comm ParallelMachine
Definition: Parallel.hpp:32
stk_classic::mesh::BulkData::get_entity
Entity * get_entity(EntityRank entity_rank, EntityId entity_id) const
Get entity with a given key.
Definition: BulkData.hpp:211
stk_classic::mesh::BulkData::modification_end
bool modification_end()
Parallel synchronization of modifications and transition to the guaranteed parallel consistent state.
Definition: BulkDataEndSync.cpp:729
stk_classic::mesh::get_entities
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
stk_classic::mesh::BulkData::declare_relation
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.
Definition: BulkDataRelation.cpp:129
stk_classic::mesh::Bucket::capacity
size_t capacity() const
Capacity of this bucket.
Definition: Bucket.hpp:122
stk_classic::mesh::fem::FEMMetaData::commit
void commit()
Commit the part and field declarations so that the meta data manager can be used to create mesh bulk ...
Definition: FEMMetaData.hpp:466
stk_classic::mesh::BulkData::entity_comm
const std::vector< Entity * > & entity_comm() const
All entities with communication information.
Definition: BulkData.hpp:367
stk_classic::mesh::Entity::log_query
EntityModificationLog log_query() const
Query the current state of the entity log.
Definition: Entity.hpp:125
stk_classic::PairIter
Definition: PairIter.hpp:21
stk_classic::mesh::fem::FEMMetaData::globally_shared_part
Part & globally_shared_part() const
Subset for the problem domain that is shared with another process. Ghost entities are not members of ...
Definition: FEMMetaData.hpp:282
stk_classic::mesh::BulkData::modification_begin
bool modification_begin()
Begin a modification phase during which the mesh bulk data could become parallel inconsistent....
Definition: BulkData.cpp:172
stk_classic::mesh::Entity::relations
PairIterRelation relations() const
All Entity relations for which this entity is a member. The relations are ordered from lowest entity-...
Definition: Entity.hpp:161
stk_classic::mesh::MetaData
The manager of an integrated collection of parts and fields.
Definition: MetaData.hpp:56
stk_classic::mesh::PartVector
std::vector< Part * > PartVector
Collections of parts are frequently maintained as a vector of Part pointers.
Definition: Types.hpp:31
stk_classic::mesh::EntityProc
std::pair< Entity *, unsigned > EntityProc
Pairing of an entity with a processor rank.
Definition: Types.hpp:111
stk_classic::mesh::Selector
This is a class for selecting buckets based on a set of meshparts and set logic.
Definition: Selector.hpp:112
stk_classic::mesh::Entity::bucket
Bucket & bucket() const
The bucket which holds this mesh entity's field data.
Definition: Entity.hpp:141
stk_classic::mesh::Entity::identifier
EntityId identifier() const
Identifier for this entity which is globally unique for a given entity type.
Definition: Entity.hpp:133
stk_classic::mesh::Entity::owner_rank
unsigned owner_rank() const
Parallel processor rank of the processor which owns this entity.
Definition: Entity.hpp:175
stk_classic::mesh::has_superset
bool has_superset(const Bucket &bucket, const unsigned &ordinal)
Is this bucket a subset of the given part by partID.
Definition: Bucket.cpp:127
stk_classic::parallel_machine_rank
unsigned parallel_machine_rank(ParallelMachine parallel_machine)
Member function parallel_machine_rank ...
Definition: Parallel.cpp:29
stk_classic::mesh::fem::FEMMetaData
FEMMetaData is a class that implements a Finite Element Method skin on top of the Sierra Tool Kit Met...
Definition: FEMMetaData.hpp:54
stk_classic::mesh::Entity
A fundamental unit within the discretization of a problem domain, including but not limited to nodes,...
Definition: Entity.hpp:120
stk_classic::mesh::fem::FEMMetaData::universal_part
Part & universal_part() const
Universal subset for the problem domain. All other parts are a subset of the universal part.
Definition: FEMMetaData.hpp:272
stk_classic::mesh::BulkData
Manager for an integrated collection of entities, entity relations, and buckets of field data.
Definition: BulkData.hpp:49