1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestStructuredGridGhostDataGenerator.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 // .NAME TestStructuredGridGhostDataGenerator.cxx -- Tests generation of ghost
16 //  data for structured grids.
17 //
18 // .SECTION Description
19 //  Serial tests for 2-D and 3-D ghost data generation of multi-block structured
20 //  grid datasets. The tests apply an XYZ field to the nodes and cells of the
21 //  domain and ensure that the created ghost data have the correct fields.
22 
23 // C++ includes
24 #include <iostream>
25 #include <sstream>
26 #include <cassert>
27 #include <string>
28 #include <vector>
29 #include <set>
30 
31 // VTK includes
32 #include "vtkDataSet.h"
33 #include "vtkStructuredGrid.h"
34 #include "vtkMultiBlockDataSet.h"
35 #include "vtkStructuredGridPartitioner.h"
36 #include "vtkStructuredGridGhostDataGenerator.h"
37 #include "vtkXMLMultiBlockDataWriter.h"
38 #include "vtkPointData.h"
39 #include "vtkCellData.h"
40 #include "vtkCell.h"
41 #include "vtkDoubleArray.h"
42 #include "vtkMathUtilities.h"
43 #include "vtkUniformGrid.h"
44 #include "vtkImageToStructuredGrid.h"
45 
46 
47 //#define DEBUG_ON
48 
49 namespace
50 {
51 
52 //------------------------------------------------------------------------------
53 // Description:
54 // Write the uniform grid multi-block dataset into an XML file.
WriteMultiBlock(vtkMultiBlockDataSet * mbds,const std::string & prefix)55 void WriteMultiBlock( vtkMultiBlockDataSet *mbds, const std::string &prefix )
56 {
57 #ifdef DEBUG_ON
58   assert( "pre: Multi-block is nullptr!" && (mbds != nullptr) );
59 
60   vtkXMLMultiBlockDataWriter *writer = vtkXMLMultiBlockDataWriter::New();
61   assert( "pre: Cannot allocate writer" && (writer != nullptr) );
62 
63   std::ostringstream oss;
64   oss.str("");
65   oss << prefix << mbds->GetNumberOfBlocks() << "."
66       << writer->GetDefaultFileExtension();
67   writer->SetFileName( oss.str().c_str() );
68   writer->SetInputData( mbds );
69   writer->Write();
70 
71   writer->Delete();
72 #else
73   (void)(prefix);
74   (void)(mbds);
75 #endif
76 }
77 
78 //------------------------------------------------------------------------------
CheckNodeFieldsForGrid(vtkStructuredGrid * grid)79 bool CheckNodeFieldsForGrid( vtkStructuredGrid *grid )
80 {
81   assert("pre: grid should not be nullptr" && (grid != nullptr) );
82   assert("pre: grid should have a NODE-XYZ array" &&
83           grid->GetPointData()->HasArray("NODE-XYZ") );
84 
85   double xyz[3];
86   vtkDoubleArray *array =
87      vtkArrayDownCast<vtkDoubleArray>( grid->GetPointData()->GetArray("NODE-XYZ") );
88   assert("pre: num tuples must match number of nodes" &&
89          (array->GetNumberOfTuples() == grid->GetNumberOfPoints()) );
90   assert("pre: num components must be 3" &&
91          (array->GetNumberOfComponents()==3));
92 
93   for( vtkIdType idx=0; idx < grid->GetNumberOfPoints(); ++idx )
94   {
95     grid->GetPoint( idx, xyz );
96 
97     for( int i=0; i < 3; ++i )
98     {
99       if( !vtkMathUtilities::FuzzyCompare(xyz[i],array->GetComponent(idx,i) ) )
100       {
101         std::cout << "Node Data mismatch: " << xyz[i] << " ";
102         std::cout <<  array->GetComponent(idx,i);
103         std::cout << std::endl;
104         std::cout.flush();
105         return false;
106       } // END if fuzzy-compare
107     } // END for all components
108   } // END for all nodes
109   return true;
110 }
111 
112 //------------------------------------------------------------------------------
CheckCellFieldsForGrid(vtkStructuredGrid * grid)113 bool CheckCellFieldsForGrid( vtkStructuredGrid *grid )
114 {
115   assert("pre: grid should not be nullptr" && (grid != nullptr) );
116   assert("pre: grid should have a NODE-XYZ array" &&
117           grid->GetCellData()->HasArray("CELL-XYZ") );
118 
119   double centroid[3];
120   double xyz[3];
121   vtkDoubleArray *array =
122       vtkArrayDownCast<vtkDoubleArray>(grid->GetCellData()->GetArray("CELL-XYZ") );
123   assert("pre: num tuples must match number of nodes" &&
124          (array->GetNumberOfTuples() == grid->GetNumberOfCells()) );
125   assert("pre: num components must be 3" &&
126          (array->GetNumberOfComponents()==3));
127 
128   vtkIdList *nodeIds = vtkIdList::New();
129   for( vtkIdType cellIdx=0; cellIdx < grid->GetNumberOfCells(); ++cellIdx )
130   {
131     nodeIds->Initialize();
132     grid->GetCellPoints(cellIdx,nodeIds);
133     double xsum = 0.0;
134     double ysum = 0.0;
135     double zsum = 0.0;
136     for( vtkIdType node=0; node < nodeIds->GetNumberOfIds(); ++node )
137     {
138       vtkIdType meshPntIdx = nodeIds->GetId( node );
139       grid->GetPoint( meshPntIdx, xyz );
140       xsum += xyz[0];
141       ysum += xyz[1];
142       zsum += xyz[2];
143     } // END for all nodes
144 
145     centroid[0] = centroid[1] = centroid[2] = 0.0;
146     centroid[0] = xsum / static_cast<double>( nodeIds->GetNumberOfIds() );
147     centroid[1] = ysum / static_cast<double>( nodeIds->GetNumberOfIds() );
148     centroid[2] = zsum / static_cast<double>( nodeIds->GetNumberOfIds() );
149 
150     for( int i=0; i < 3; ++i )
151     {
152       if( !vtkMathUtilities::FuzzyCompare(
153           centroid[i],array->GetComponent(cellIdx,i)) )
154       {
155         std::cout << "Cell Data mismatch: " << centroid[i] << " ";
156         std::cout <<  array->GetComponent(cellIdx,i);
157         std::cout << std::endl;
158         std::cout.flush();
159         nodeIds->Delete();
160         return false;
161       } // END if fuzz-compare
162     } // END for all components
163   } // END for all cells
164   nodeIds->Delete();
165   return true;
166 }
167 
168 //------------------------------------------------------------------------------
CheckFields(vtkMultiBlockDataSet * mbds,bool hasNodeData,bool hasCellData)169 int CheckFields( vtkMultiBlockDataSet *mbds,bool hasNodeData,bool hasCellData )
170 {
171   assert("pre: input multi-block is nullptr" && (mbds != nullptr) );
172 
173   if( !hasNodeData && !hasCellData )
174   {
175     return 0;
176   }
177 
178   for(unsigned int block=0; block < mbds->GetNumberOfBlocks(); ++block )
179   {
180     vtkStructuredGrid *grid =
181         vtkStructuredGrid::SafeDownCast(mbds->GetBlock(block));
182     assert("pre: grid is not nullptr" && (grid != nullptr) );
183 
184     if( hasNodeData )
185     {
186       if( !CheckNodeFieldsForGrid( grid ) )
187       {
188         return 1;
189       }
190     }
191 
192     if( hasCellData )
193     {
194       if( !CheckCellFieldsForGrid( grid ) )
195       {
196         std::cout << "CheckCellFieldsForGrid failed for block " << block << "\n";
197         std::cout.flush();
198         return 1;
199       }
200     }
201 
202   } // END for all blocks
203 
204   return 0;
205 }
206 
207 //------------------------------------------------------------------------------
208 // Description:
209 // Adds and XYZ vector field in the nodes of the data-set
AddNodeCenteredXYZField(vtkMultiBlockDataSet * mbds)210 void AddNodeCenteredXYZField( vtkMultiBlockDataSet *mbds )
211 {
212   assert("pre: Multi-block is nullptr!" && (mbds != nullptr) );
213 
214   for( unsigned int block=0; block < mbds->GetNumberOfBlocks(); ++block )
215   {
216     vtkStructuredGrid *grid =
217         vtkStructuredGrid::SafeDownCast(mbds->GetBlock(block));
218     assert("pre: grid is nullptr for the given block" && (grid != nullptr) );
219 
220     vtkDoubleArray *nodeXYZArray = vtkDoubleArray::New();
221     nodeXYZArray->SetName( "NODE-XYZ" );
222     nodeXYZArray->SetNumberOfComponents( 3 );
223     nodeXYZArray->SetNumberOfTuples( grid->GetNumberOfPoints() );
224 
225     double xyz[3];
226     for( vtkIdType pntIdx=0; pntIdx < grid->GetNumberOfPoints(); ++pntIdx )
227     {
228       grid->GetPoint( pntIdx, xyz );
229       nodeXYZArray->SetComponent( pntIdx, 0, xyz[0] );
230       nodeXYZArray->SetComponent( pntIdx, 1, xyz[1] );
231       nodeXYZArray->SetComponent( pntIdx, 2, xyz[2] );
232     } // END for all points
233 
234     grid->GetPointData()->AddArray( nodeXYZArray );
235     nodeXYZArray->Delete();
236   } // END for all blocks
237 
238 }
239 
240 //------------------------------------------------------------------------------
241 // Description:
242 // Adds and XYZ vector field in the nodes of the dataset
AddCellCenteredXYZField(vtkMultiBlockDataSet * mbds)243 void AddCellCenteredXYZField( vtkMultiBlockDataSet *mbds )
244 {
245   assert("pre: Multi-block is nullptr!" && (mbds != nullptr) );
246 
247   for( unsigned int block=0; block < mbds->GetNumberOfBlocks(); ++block )
248   {
249     vtkStructuredGrid *grid =
250         vtkStructuredGrid::SafeDownCast(mbds->GetBlock(block));
251     assert("pre: grid is nullptr for the given block" && (grid != nullptr) );
252 
253     vtkDoubleArray *cellXYZArray = vtkDoubleArray::New();
254     cellXYZArray->SetName( "CELL-XYZ" );
255     cellXYZArray->SetNumberOfComponents( 3 );
256     cellXYZArray->SetNumberOfTuples( grid->GetNumberOfCells() );
257 
258     double centroid[3];
259     double xyz[3];
260     for( vtkIdType cellIdx=0; cellIdx < grid->GetNumberOfCells(); ++cellIdx )
261     {
262       vtkCell *c = grid->GetCell( cellIdx );
263       assert( "pre: cell is not nullptr" && (c != nullptr) );
264 
265       double xsum = 0.0;
266       double ysum = 0.0;
267       double zsum = 0.0;
268       for( vtkIdType node=0; node < c->GetNumberOfPoints(); ++node )
269       {
270         vtkIdType meshPntIdx = c->GetPointId( node );
271         grid->GetPoint( meshPntIdx, xyz );
272         xsum += xyz[0];
273         ysum += xyz[1];
274         zsum += xyz[2];
275       } // END for all nodes
276 
277       centroid[0] = xsum / c->GetNumberOfPoints();
278       centroid[1] = ysum / c->GetNumberOfPoints();
279       centroid[2] = zsum / c->GetNumberOfPoints();
280 
281       cellXYZArray->SetComponent( cellIdx, 0, centroid[0] );
282       cellXYZArray->SetComponent( cellIdx, 1, centroid[1] );
283       cellXYZArray->SetComponent( cellIdx, 2, centroid[2] );
284     } // END for all cells
285 
286     grid->GetCellData()->AddArray( cellXYZArray );
287     cellXYZArray->Delete();
288   } // END for all blocks
289 }
290 
291 //------------------------------------------------------------------------------
292 // Description:
293 // Creates a test data-set.
GetDataSet(double globalOrigin[3],int WholeExtent[6],double gridSpacing[3],const int numPartitions,const int numGhosts,const bool AddNodeData,const bool AddCellData)294 vtkMultiBlockDataSet* GetDataSet(
295     double globalOrigin[3], int WholeExtent[6],double gridSpacing[3],
296     const int numPartitions, const int numGhosts,
297     const bool AddNodeData, const bool AddCellData )
298 {
299   // STEP 0: Get the global grid dimensions
300   int dims[3];
301   vtkStructuredData::GetDimensionsFromExtent( WholeExtent, dims );
302 
303   // STEP 1: Get the whole grid as a uniform grid instance
304   vtkUniformGrid *wholeGrid = vtkUniformGrid::New();
305   wholeGrid->SetOrigin( globalOrigin );
306   wholeGrid->SetSpacing( gridSpacing );
307   wholeGrid->SetDimensions( dims );
308 
309   // STEP 2: Conver to structured grid
310   vtkImageToStructuredGrid *img2sgrid = vtkImageToStructuredGrid::New();
311   assert("pre:" && (img2sgrid != nullptr));
312   img2sgrid->SetInputData( wholeGrid );
313   img2sgrid->Update();
314   vtkStructuredGrid *wholeStructuredGrid = vtkStructuredGrid::New();
315   wholeStructuredGrid->DeepCopy( img2sgrid->GetOutput() );
316   img2sgrid->Delete();
317   wholeGrid->Delete();
318 
319   // STEP 3: Partition the structured grid domain
320   vtkStructuredGridPartitioner *gridPartitioner =
321       vtkStructuredGridPartitioner::New();
322   gridPartitioner->SetInputData( wholeStructuredGrid );
323   gridPartitioner->SetNumberOfPartitions( numPartitions );
324   gridPartitioner->SetNumberOfGhostLayers( numGhosts );
325   gridPartitioner->Update();
326 
327   // STEP 4: Get the partitioned dataset
328   vtkMultiBlockDataSet *mbds =
329       vtkMultiBlockDataSet::SafeDownCast(gridPartitioner->GetOutput() );
330   mbds->SetReferenceCount( mbds->GetReferenceCount()+1 );
331 
332   // STEP 5: Delete temporary data
333   wholeStructuredGrid->Delete();
334   gridPartitioner->Delete();
335 
336   // STEP 6: Add node-centered and cell-centered fields
337   if( AddNodeData )
338   {
339    AddNodeCenteredXYZField( mbds );
340   }
341 
342   if( AddCellData )
343   {
344    AddCellCenteredXYZField( mbds );
345   }
346 
347   return( mbds );
348 }
349 
350 //------------------------------------------------------------------------------
351 // Description:
352 // Test 2-D StructuredGridGhostDataGenerator
Test2D(const bool hasNodeData,const bool hasCellData,const int numPartitions,const int numGhosts)353 int Test2D(
354     const bool hasNodeData, const bool hasCellData,
355     const int numPartitions, const int numGhosts )
356 {
357   std::cout << "===================\n";
358   std::cout << "Testing 2-D ghost generation....\n";
359   std::cout << "Number of Partitions: " << numPartitions << std::endl;
360   std::cout << "Number of ghost-layers in the input: " << numGhosts << "\n";
361   std::cout << "Number of ghost-layers to be generated: 1\n";
362   std::cout << "Node-centered data: ";
363   if( hasNodeData )
364   {
365     std::cout << "Yes\n";
366   }
367   else
368   {
369     std::cout << "No\n";
370   }
371   std::cout << "Cell-centered data: ";
372   if( hasCellData )
373   {
374     std::cout << "Yes\n";
375   }
376   else
377   {
378     std::cout << "No\n";
379   }
380   std::cout.flush();
381 
382   int rc = 0;
383 
384   int WholeExtent[6] = {0,49,0,49,0,0};
385   double h[3] = {0.5,0.5,0.5};
386   double p[3] = {0.0,0.0,0.0};
387 
388   vtkMultiBlockDataSet *mbds = GetDataSet(p,WholeExtent,h,
389       numPartitions,numGhosts,hasNodeData,hasCellData);
390   WriteMultiBlock( mbds, "STRUCTUREDINITIAL");
391 
392   vtkStructuredGridGhostDataGenerator *ghostDataGenerator =
393       vtkStructuredGridGhostDataGenerator::New();
394 
395   ghostDataGenerator->SetInputData( mbds );
396   ghostDataGenerator->SetNumberOfGhostLayers( 1 );
397   ghostDataGenerator->Update();
398 
399   vtkMultiBlockDataSet *ghostDataSet = ghostDataGenerator->GetOutput();
400  WriteMultiBlock( ghostDataSet, "STRUCTUREDGHOSTED" );
401 
402   rc = CheckFields( ghostDataSet, hasNodeData, hasCellData );
403   mbds->Delete();
404   ghostDataGenerator->Delete();
405   return( rc );
406 }
407 
408 //------------------------------------------------------------------------------
409 // Description:
410 // Test 2-D StructuredGridGhostDataGenerator
Test3D(const bool hasNodeData,const bool hasCellData,const int numPartitions,const int numGhosts)411 int Test3D(
412     const bool hasNodeData, const bool hasCellData,
413     const int numPartitions, const int numGhosts )
414 {
415   std::cout << "===================\n";
416   std::cout << "Testing 3-D ghost generation....\n";
417   std::cout << "Number of Partitions: " << numPartitions << std::endl;
418   std::cout << "Number of ghost-layers in the input: " << numGhosts << "\n";
419   std::cout << "Number of ghost-layers to be generated: 1\n";
420   std::cout << "Node-centered data: ";
421   if( hasNodeData )
422   {
423     std::cout << "Yes\n";
424   }
425   else
426   {
427     std::cout << "No\n";
428   }
429   std::cout << "Cell-centered data: ";
430   if( hasCellData )
431   {
432     std::cout << "Yes\n";
433   }
434   else
435   {
436     std::cout << "No\n";
437   }
438   std::cout.flush();
439 
440   int rc = 0;
441   int WholeExtent[6] = {0,49,0,49,0,49};
442   double h[3] = {0.5,0.5,0.5};
443   double p[3] = {0.0,0.0,0.0};
444 
445   vtkMultiBlockDataSet *mbds = GetDataSet(p,WholeExtent,h,
446       numPartitions,numGhosts,hasNodeData,hasCellData);
447   WriteMultiBlock( mbds, "STRUCTUREDINITIAL");
448 
449   vtkStructuredGridGhostDataGenerator *ghostDataGenerator =
450       vtkStructuredGridGhostDataGenerator::New();
451   ghostDataGenerator->SetInputData( mbds );
452   ghostDataGenerator->SetNumberOfGhostLayers( 1 );
453   ghostDataGenerator->Update();
454 
455  vtkMultiBlockDataSet *ghostedDataSet = ghostDataGenerator->GetOutput();
456  WriteMultiBlock( ghostedDataSet, "STRUCTUREDGHOSTED" );
457 
458   rc = CheckFields( ghostedDataSet, hasNodeData, hasCellData );
459   mbds->Delete();
460   ghostDataGenerator->Delete();
461 
462   return( rc );
463 }
464 
465 //------------------------------------------------------------------------------
466 // Description:
467 // Tests StructuredGridGhostDataGenerator
TestStructuredGridGhostDataGenerator_internal(int,char * [])468 int TestStructuredGridGhostDataGenerator_internal(int, char *[])
469 {
470   int rc = 0;
471   rc += Test2D(false,false,4,0);
472   rc += Test2D(true,false,4,0);
473   rc += Test2D(false,true,4,0);
474   rc += Test2D(true,true,4,0);
475 
476   rc += Test3D(true,false,32,0);
477   rc += Test3D(false,true,32,0);
478   rc += Test3D(true,true,32,0);
479   return( rc );
480 }
481 
482 }
483 
484 //------------------------------------------------------------------------------
485 // Description:
486 // Program main
TestStructuredGridGhostDataGenerator(int argc,char * argv[])487 int TestStructuredGridGhostDataGenerator( int argc, char *argv[] )
488 {
489   if( argc==1 )
490   {
491     return( TestStructuredGridGhostDataGenerator_internal(argc, argv) );
492   }
493   int rc             = 0;
494   int NumBlocks      = 0;
495   int NumGhostLayers = 0;
496   if( argc != 3 )
497   {
498     std::cout << "Usage: ./bin/TestStructuredGridGhostDataGenerator <N> <NG>\n";
499     std::cout.flush();
500     return 0;
501   }
502 
503   NumBlocks      = atoi(argv[1]);
504   NumGhostLayers = atoi(argv[2]);
505 
506   std::cout << "Running 2-D Test with just geometry...";
507   std::cout.flush();
508   rc += Test2D(false,false,NumBlocks,NumGhostLayers);
509   if( rc == 0 )
510   {
511     std::cout << "[OK]\n";
512     std::cout.flush();
513   }
514   else
515   {
516     std::cout << "FAILED!!!!\n";
517     std::cout.flush();
518   }
519 
520 
521   std::cout << "Running 2-D Test with node fields...";
522   std::cout.flush();
523   rc += Test2D(true, false, NumBlocks, NumGhostLayers );
524   if( rc == 0 )
525   {
526     std::cout << "[OK]\n";
527     std::cout.flush();
528   }
529   else
530   {
531     std::cout << "FAILED!!!!\n";
532     std::cout.flush();
533   }
534 
535   std::cout << "Running 2-D Test with both cell/node fields...";
536   std::cout.flush();
537   rc += Test2D(true,true,NumBlocks,NumGhostLayers);
538   if( rc == 0 )
539   {
540     std::cout << "[OK]\n";
541     std::cout.flush();
542   }
543   else
544   {
545     std::cout << "FAILED!!!!\n";
546     std::cout.flush();
547   }
548   return 0;
549 }
550 
551