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