1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestGradientAndVorticity.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 /*----------------------------------------------------------------------------
16  Copyright (c) Sandia Corporation
17  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
18 ----------------------------------------------------------------------------*/
19 
20 #include "vtkArrayCalculator.h"
21 #include "vtkCell.h"
22 #include "vtkCellData.h"
23 #include "vtkDoubleArray.h"
24 #include "vtkPointData.h"
25 #include "vtkSmartPointer.h"
26 #include "vtkStdString.h"
27 #include "vtkStructuredGrid.h"
28 #include "vtkStructuredGridReader.h"
29 #include "vtkUnstructuredGrid.h"
30 #include "vtkUnstructuredGridReader.h"
31 #include "vtkmGradient.h"
32 
33 #include <vector>
34 #include <vtkm/testing/Testing.h>
35 
36 #define VTK_CREATE(type, var) vtkSmartPointer<type> var = vtkSmartPointer<type>::New()
37 
38 // The 3D cell with the maximum number of points is VTK_LAGRANGE_HEXAHEDRON.
39 // We support up to 6th order hexahedra.
40 #define VTK_MAXIMUM_NUMBER_OF_POINTS 216
41 
42 namespace
43 {
44 double Tolerance = 0.00001;
45 
46 //------------------------------------------------------------------------------
CreateCellData(vtkDataSet * grid,int numberOfComponents,int offset,const char * arrayName)47 void CreateCellData(vtkDataSet* grid, int numberOfComponents, int offset, const char* arrayName)
48 {
49   vtkIdType numberOfCells = grid->GetNumberOfCells();
50   VTK_CREATE(vtkDoubleArray, array);
51   array->SetNumberOfComponents(numberOfComponents);
52   array->SetNumberOfTuples(numberOfCells);
53   std::vector<double> tupleValues(numberOfComponents);
54   double point[3], parametricCenter[3], weights[VTK_MAXIMUM_NUMBER_OF_POINTS];
55   for (vtkIdType i = 0; i < numberOfCells; i++)
56   {
57     vtkCell* cell = grid->GetCell(i);
58     cell->GetParametricCenter(parametricCenter);
59     int subId = 0;
60     cell->EvaluateLocation(subId, parametricCenter, point, weights);
61     for (int j = 0; j < numberOfComponents; j++)
62     { // +offset makes the curl/vorticity nonzero
63       tupleValues[j] = point[(j + offset) % 3];
64     }
65     array->SetTypedTuple(i, &tupleValues[0]);
66   }
67   array->SetName(arrayName);
68   grid->GetCellData()->AddArray(array);
69 }
70 
71 //------------------------------------------------------------------------------
CreatePointData(vtkDataSet * grid,int numberOfComponents,int offset,const char * arrayName)72 void CreatePointData(vtkDataSet* grid, int numberOfComponents, int offset, const char* arrayName)
73 {
74   vtkIdType numberOfPoints = grid->GetNumberOfPoints();
75   VTK_CREATE(vtkDoubleArray, array);
76   array->SetNumberOfComponents(numberOfComponents);
77   array->SetNumberOfTuples(numberOfPoints);
78   std::vector<double> tupleValues(numberOfComponents);
79   double point[3];
80   for (vtkIdType i = 0; i < numberOfPoints; i++)
81   {
82     grid->GetPoint(i, point);
83     for (int j = 0; j < numberOfComponents; j++)
84     { // +offset makes the curl/vorticity nonzero
85       tupleValues[j] = point[(j + offset) % 3];
86     }
87     array->SetTypedTuple(i, &tupleValues[0]);
88   }
89   array->SetName(arrayName);
90   grid->GetPointData()->AddArray(array);
91 }
92 
93 //------------------------------------------------------------------------------
IsGradientCorrect(vtkDoubleArray * gradients,vtkDoubleArray * correct)94 int IsGradientCorrect(vtkDoubleArray* gradients, vtkDoubleArray* correct)
95 {
96   int numberOfComponents = gradients->GetNumberOfComponents();
97   for (vtkIdType i = 0; i < gradients->GetNumberOfTuples(); i++)
98   {
99     bool invalid = false;
100     for (int j = 0; j < numberOfComponents; j++)
101     {
102       double value = gradients->GetTypedComponent(i, j);
103       double expected = correct->GetTypedComponent(i, j);
104 
105       if ((value - expected) > Tolerance)
106       {
107         invalid = true;
108       }
109     }
110 
111     if (invalid)
112     {
113       std::vector<double> values;
114       values.resize(numberOfComponents);
115       std::vector<double> expected;
116       expected.resize(numberOfComponents);
117 
118       gradients->GetTypedTuple(i, values.data());
119       correct->GetTypedTuple(i, expected.data());
120 
121       std::cout << "Gradient[ i ] should look like: " << std::endl;
122       std::cout << expected[0] << ", " << expected[1] << ", " << expected[2] << std::endl;
123       if (numberOfComponents > 3)
124       {
125         std::cout << expected[3] << ", " << expected[4] << ", " << expected[5] << std::endl;
126         std::cout << expected[6] << ", " << expected[7] << ", " << expected[8] << std::endl;
127       }
128 
129       std::cout << "Gradient[ i ] actually looks like: " << std::endl;
130       std::cout << values[0] << ", " << values[1] << ", " << values[2] << std::endl;
131       if (numberOfComponents > 3)
132       {
133         std::cout << values[3] << ", " << values[4] << ", " << values[5] << std::endl;
134         std::cout << values[6] << ", " << values[7] << ", " << values[8] << std::endl;
135       }
136       std::cout << std::endl;
137     }
138 
139     if (i > 10 && invalid)
140     {
141       return 0;
142     }
143   }
144   return 1;
145 }
146 
147 //------------------------------------------------------------------------------
148 // we assume that the gradients are correct and so we can compute the "real"
149 // vorticity from it
IsVorticityCorrect(vtkDoubleArray * gradients,vtkDoubleArray * vorticity)150 int IsVorticityCorrect(vtkDoubleArray* gradients, vtkDoubleArray* vorticity)
151 {
152   if (gradients->GetNumberOfComponents() != 9 || vorticity->GetNumberOfComponents() != 3)
153   {
154     vtkGenericWarningMacro("Bad number of components.");
155     return 0;
156   }
157   for (vtkIdType i = 0; i < gradients->GetNumberOfTuples(); i++)
158   {
159     double* g = gradients->GetTuple(i);
160     double* v = vorticity->GetTuple(i);
161     if (!test_equal(v[0], g[7] - g[5]))
162     {
163       vtkGenericWarningMacro("Bad vorticity[0] value "
164         << v[0] << " " << g[7] - g[5] << " difference is " << (v[0] - g[7] + g[5]));
165       return 0;
166     }
167     else if (!test_equal(v[1], g[2] - g[6]))
168     {
169       vtkGenericWarningMacro("Bad vorticity[1] value "
170         << v[1] << " " << g[2] - g[6] << " difference is " << (v[1] - g[2] + g[6]));
171       return 0;
172     }
173     else if (!test_equal(v[2], g[3] - g[1]))
174     {
175       vtkGenericWarningMacro("Bad vorticity[2] value "
176         << v[2] << " " << g[3] - g[1] << " difference is " << (v[2] - g[3] + g[1]));
177       return 0;
178     }
179   }
180 
181   return 1;
182 }
183 
184 //------------------------------------------------------------------------------
185 // we assume that the gradients are correct and so we can compute the "real"
186 // Q criterion from it
IsQCriterionCorrect(vtkDoubleArray * gradients,vtkDoubleArray * qCriterion)187 int IsQCriterionCorrect(vtkDoubleArray* gradients, vtkDoubleArray* qCriterion)
188 {
189   if (gradients->GetNumberOfComponents() != 9 || qCriterion->GetNumberOfComponents() != 1)
190   {
191     vtkGenericWarningMacro("Bad number of components.");
192     return 0;
193   }
194   for (vtkIdType i = 0; i < gradients->GetNumberOfTuples(); i++)
195   {
196     double* g = gradients->GetTuple(i);
197     double qc = qCriterion->GetValue(i);
198 
199     double t1 = .25 *
200       ((g[7] - g[5]) * (g[7] - g[5]) + (g[3] - g[1]) * (g[3] - g[1]) +
201         (g[2] - g[6]) * (g[2] - g[6]));
202     double t2 = .5 *
203       (g[0] * g[0] + g[4] * g[4] + g[8] * g[8] +
204         .5 *
205           ((g[3] + g[1]) * (g[3] + g[1]) + (g[6] + g[2]) * (g[6] + g[2]) +
206             (g[7] + g[5]) * (g[7] + g[5])));
207 
208     if (!test_equal(qc, t1 - t2))
209     {
210       vtkGenericWarningMacro(
211         "Bad Q-criterion value " << qc << " " << t1 - t2 << " difference is " << (qc - t1 + t2));
212       return 0;
213     }
214   }
215 
216   return 1;
217 }
218 
219 //------------------------------------------------------------------------------
220 // we assume that the gradients are correct and so we can compute the "real"
221 // divergence from it
IsDivergenceCorrect(vtkDoubleArray * gradients,vtkDoubleArray * divergence)222 int IsDivergenceCorrect(vtkDoubleArray* gradients, vtkDoubleArray* divergence)
223 {
224   if (gradients->GetNumberOfComponents() != 9 || divergence->GetNumberOfComponents() != 1)
225   {
226     vtkGenericWarningMacro("Bad number of components.");
227     return 0;
228   }
229   for (vtkIdType i = 0; i < gradients->GetNumberOfTuples(); i++)
230   {
231     double* g = gradients->GetTuple(i);
232     double div = divergence->GetValue(i);
233     double gValue = g[0] + g[4] + g[8];
234 
235     if (!test_equal(div, gValue))
236     {
237       vtkGenericWarningMacro(
238         "Bad divergence value " << div << " " << gValue << " difference is " << (div - gValue));
239       return 0;
240     }
241   }
242 
243   return 1;
244 }
245 
246 //------------------------------------------------------------------------------
PerformTest(vtkDataSet * grid)247 int PerformTest(vtkDataSet* grid)
248 {
249   // Cleaning out the existing field data so that I can replace it with
250   // an analytic function that I know the gradient of
251   grid->GetPointData()->Initialize();
252   grid->GetCellData()->Initialize();
253   const char fieldName[] = "LinearField";
254   int offset = 1;
255   const int numberOfComponents = 3;
256   CreateCellData(grid, numberOfComponents, offset, fieldName);
257   CreatePointData(grid, numberOfComponents, offset, fieldName);
258 
259   const char resultName[] = "Result";
260 
261   VTK_CREATE(vtkmGradient, cellGradients);
262   cellGradients->ForceVTKmOn();
263   cellGradients->SetInputData(grid);
264   cellGradients->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_CELLS, fieldName);
265   cellGradients->SetResultArrayName(resultName);
266 
267   VTK_CREATE(vtkGradientFilter, correctCellGradients);
268   correctCellGradients->SetInputData(grid);
269   correctCellGradients->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_CELLS, fieldName);
270   correctCellGradients->SetResultArrayName(resultName);
271 
272   VTK_CREATE(vtkmGradient, pointGradients);
273   pointGradients->ForceVTKmOn();
274   pointGradients->SetInputData(grid);
275   pointGradients->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldName);
276   pointGradients->SetResultArrayName(resultName);
277 
278   VTK_CREATE(vtkGradientFilter, correctPointGradients);
279   correctPointGradients->SetInputData(grid);
280   correctPointGradients->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldName);
281   correctPointGradients->SetResultArrayName(resultName);
282 
283   cellGradients->Update();
284   pointGradients->Update();
285 
286   correctCellGradients->Update();
287   correctPointGradients->Update();
288 
289   vtkDoubleArray* gradCellArray = vtkArrayDownCast<vtkDoubleArray>(
290     vtkDataSet::SafeDownCast(cellGradients->GetOutput())->GetCellData()->GetArray(resultName));
291 
292   vtkDoubleArray* correctCellArray =
293     vtkArrayDownCast<vtkDoubleArray>(vtkDataSet::SafeDownCast(correctCellGradients->GetOutput())
294                                        ->GetCellData()
295                                        ->GetArray(resultName));
296 
297   if (!grid->IsA("vtkStructuredGrid"))
298   {
299     // ignore cell gradients on structured grids as the version for
300     // VTK-m differs from VTK. Once VTK-m is able to do stencil based
301     // gradients for point and cells, we can enable this check.
302     if (!IsGradientCorrect(gradCellArray, correctCellArray))
303     {
304       return EXIT_FAILURE;
305     }
306   }
307 
308   vtkDoubleArray* gradPointArray = vtkArrayDownCast<vtkDoubleArray>(
309     vtkDataSet::SafeDownCast(pointGradients->GetOutput())->GetPointData()->GetArray(resultName));
310 
311   vtkDoubleArray* correctPointArray =
312     vtkArrayDownCast<vtkDoubleArray>(vtkDataSet::SafeDownCast(correctPointGradients->GetOutput())
313                                        ->GetPointData()
314                                        ->GetArray(resultName));
315 
316   if (!IsGradientCorrect(gradPointArray, correctPointArray))
317   {
318     return EXIT_FAILURE;
319   }
320 
321   // now check on the vorticity calculations
322   VTK_CREATE(vtkmGradient, cellVorticity);
323   cellVorticity->ForceVTKmOn();
324   cellVorticity->SetInputData(grid);
325   cellVorticity->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_CELLS, fieldName);
326   cellVorticity->SetResultArrayName(resultName);
327   cellVorticity->SetComputeVorticity(1);
328   cellVorticity->Update();
329 
330   VTK_CREATE(vtkmGradient, pointVorticity);
331   pointVorticity->ForceVTKmOn();
332   pointVorticity->SetInputData(grid);
333   pointVorticity->SetInputScalars(vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldName);
334   pointVorticity->SetResultArrayName(resultName);
335   pointVorticity->SetComputeVorticity(1);
336   pointVorticity->SetComputeQCriterion(1);
337   pointVorticity->SetComputeDivergence(1);
338   pointVorticity->Update();
339 
340   // cell stuff
341   vtkDoubleArray* vorticityCellArray = vtkArrayDownCast<vtkDoubleArray>(
342     vtkDataSet::SafeDownCast(cellVorticity->GetOutput())->GetCellData()->GetArray("Vorticity"));
343   if (!IsVorticityCorrect(gradCellArray, vorticityCellArray))
344   {
345     return EXIT_FAILURE;
346   }
347 
348   // point stuff
349   vtkDoubleArray* vorticityPointArray = vtkArrayDownCast<vtkDoubleArray>(
350     vtkDataSet::SafeDownCast(pointVorticity->GetOutput())->GetPointData()->GetArray("Vorticity"));
351   if (!IsVorticityCorrect(gradPointArray, vorticityPointArray))
352   {
353     return EXIT_FAILURE;
354   }
355 
356   vtkDoubleArray* divergencePointArray = vtkArrayDownCast<vtkDoubleArray>(
357     vtkDataSet::SafeDownCast(pointVorticity->GetOutput())->GetPointData()->GetArray("Divergence"));
358   if (!IsDivergenceCorrect(gradPointArray, divergencePointArray))
359   {
360     return EXIT_FAILURE;
361   }
362 
363   vtkDoubleArray* qCriterionPointArray = vtkArrayDownCast<vtkDoubleArray>(
364     vtkDataSet::SafeDownCast(pointVorticity->GetOutput())->GetPointData()->GetArray("Q-criterion"));
365   if (!IsQCriterionCorrect(gradPointArray, qCriterionPointArray))
366   {
367     return EXIT_FAILURE;
368   }
369 
370   return EXIT_SUCCESS;
371 }
372 } // end local namespace
373 
374 //------------------------------------------------------------------------------
TestVTKMGradientAndVorticity(int argc,char * argv[])375 int TestVTKMGradientAndVorticity(int argc, char* argv[])
376 {
377   int i;
378   // Need to get the data root.
379   const char* data_root = nullptr;
380   for (i = 0; i < argc - 1; i++)
381   {
382     if (strcmp("-D", argv[i]) == 0)
383     {
384       data_root = argv[i + 1];
385       break;
386     }
387   }
388   if (!data_root)
389   {
390     vtkGenericWarningMacro("Need to specify the directory to VTK_DATA_ROOT with -D <dir>.");
391     return EXIT_FAILURE;
392   }
393 
394   vtkStdString filename(std::string(data_root) + "/Data/SampleStructGrid.vtk");
395   VTK_CREATE(vtkStructuredGridReader, structuredGridReader);
396   structuredGridReader->SetFileName(filename.c_str());
397   structuredGridReader->Update();
398   vtkDataSet* grid = vtkDataSet::SafeDownCast(structuredGridReader->GetOutput());
399 
400   if (PerformTest(grid))
401   {
402     return EXIT_FAILURE;
403   }
404 
405   // convert the structured grid to an unstructured grid
406   VTK_CREATE(vtkUnstructuredGrid, ug);
407   ug->SetPoints(vtkStructuredGrid::SafeDownCast(grid)->GetPoints());
408   ug->Allocate(grid->GetNumberOfCells());
409   for (vtkIdType id = 0; id < grid->GetNumberOfCells(); id++)
410   {
411     vtkCell* cell = grid->GetCell(id);
412     ug->InsertNextCell(cell->GetCellType(), cell->GetPointIds());
413   }
414 
415   return PerformTest(ug);
416 }
417