NEMoSys  0.63.0
A modular, extensible resource with robust automated mesh generation, mesh quality analysis, adaptive mesh refinement, and data transfer between arbitrary meshes.
Cubature.C
Go to the documentation of this file.
1 /*******************************************************************************
2 * Promesh *
3 * Copyright (C) 2022, IllinoisRocstar LLC. All rights reserved. *
4 * *
5 * Promesh is the property of IllinoisRocstar LLC. *
6 * *
7 * IllinoisRocstar LLC *
8 * Champaign, IL *
9 * www.illinoisrocstar.com *
10 * promesh@illinoisrocstar.com *
11 *******************************************************************************/
12 /*******************************************************************************
13 * This file is part of Promesh *
14 * *
15 * This version of Promesh is free software: you can redistribute it and/or *
16 * modify it under the terms of the GNU Lesser General Public License as *
17 * published by the Free Software Foundation, either version 3 of the License, *
18 * or (at your option) any later version. *
19 * *
20 * Promesh is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more *
23 * details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with this program. If not, see <https://www.gnu.org/licenses/>. *
27 * *
28 *******************************************************************************/
29 #include "Integration/Cubature.H"
30 
31 #include <vtkQuadraturePointsGenerator.h>
32 #include <vtkMeshQuality.h>
33 #include <vtkInformationQuadratureSchemeDefinitionVectorKey.h>
34 #include <vtkCellData.h>
35 #include <vtkCellTypes.h>
36 #include <vtkInformation.h>
37 #include <vtkXMLPolyDataWriter.h>
38 #include "Mesh/vtkMesh.H" // for writeVTFile
39 
40 #ifdef HAVE_OPENMP
41 #include <omp.h>
42 #endif
43 
44 #include "AuxiliaryFunctions.H"
45 #include <iostream>
46 
47 // Table 10.4 Quadrature for unit tetrahedra in http://me.rice.edu/~akin/Elsevier/Chap_10.pdf
48 // OR
49 // Table 6.3.1 and 6.3.1 in http://www.cs.rpi.edu/~flaherje/pdf/fea6.pdf
50 // also
51 // https://www.colorado.edu/engineering/CAS/courses.d/AFEM.d/AFEM.AppI.d/AFEM.AppI.pdf
52 
53 // The following arrays are shape functions evaluated at quadrature points supporting second
54 // order integration for the indicated cell type as well as the quadrature point weights.
55 
56 // TODO: Add a check to preserve cubature information on mesh for future use
57 // TODO: Add L2-norm function to simplify implementation in other classes where this is used
58 double TRI3[] =
59  {
60  .666666666666667, .166666666666667, .166666666666667,
61  .166666666666667, .666666666666667, .166666666666667,
62  .166666666666667, .166666666666667, .666666666666667
63  };
64 //double TRI3W[] = {0.166666666666666, 0.166666666666666, 0.166666666666666};
65 double TRI3W[] = {0.333333333333333, 0.333333333333333, 0.333333333333333};
66 
67 // second order
68 double TET4[] =
69  {
70  .585410196624969, .138196601125011, .138196601125011, .138196601125011,
71  .138196601125011, .585410196624969, .138196601125011, .138196601125011,
72  .138196601125011, .138196601125011, .585410196624969, .138196601125011,
73  .138196601125011, .138196601125011, .138196601125011, .585410196624969
74  };
75 //double TET4W[] = {0.041666666666667, 0.041666666666667,
76 // 0.041666666666667, 0.041666666666667};
77 double TET4W[] = {0.25, 0.25, 0.25, 0.25};
78 
79 double HEX8[] =
80  {
81  0.490562612162344, 0.131445855765802, 0.0352208109008645,
82  0.131445855765802, 0.131445855765802, 0.0352208109008645,
83  0.00943738783765592, 0.0352208109008645, 0.131445855765802,
84  0.490562612162344, 0.131445855765802, 0.0352208109008645,
85  0.0352208109008645, 0.131445855765802, 0.0352208109008645,
86  0.00943738783765592, 0.131445855765802, 0.0352208109008645,
87  0.131445855765802, 0.490562612162344, 0.0352208109008645,
88  0.00943738783765592, 0.0352208109008645, 0.131445855765802,
89  0.0352208109008645, 0.131445855765802, 0.490562612162344,
90  0.131445855765802, 0.00943738783765592, 0.0352208109008645,
91  0.131445855765802, 0.0352208109008645, 0.131445855765802,
92  0.0352208109008645, 0.00943738783765592, 0.0352208109008645,
93  0.490562612162344, 0.131445855765802, 0.0352208109008645,
94  0.131445855765802, 0.0352208109008645, 0.131445855765802,
95  0.0352208109008645, 0.00943738783765592, 0.131445855765802,
96  0.490562612162344, 0.131445855765802, 0.0352208109008645,
97  0.0352208109008645, 0.00943738783765592, 0.0352208109008645,
98  0.131445855765802, 0.131445855765802, 0.0352208109008645,
99  0.131445855765802, 0.490562612162344, 0.00943738783765592,
100  0.0352208109008645, 0.131445855765802, 0.0352208109008645,
101  0.0352208109008645, 0.131445855765802, 0.490562612162344,
102  0.131445855765802
103  };
104 double HEX8W[] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
105 
106 
107 GaussCubature::GaussCubature(vtkDataSet *_dataSet)
108  : dataSet(_dataSet), numVolCells(0), totalComponents(0)
109 {
110  dataSet->GetCellData()->RemoveArray("QuadratureOffSet");
112 }
113 
114 GaussCubature::GaussCubature(vtkDataSet *_dataSet,
115  const std::vector<int> &_arrayIDs)
116  : dataSet(_dataSet), numVolCells(0), arrayIDs(_arrayIDs),
117  totalComponents(0)
118 {
119  dataSet->GetCellData()->RemoveArray("QuadratureOffSet");
122 }
123 
124 GaussCubature *GaussCubature::Create(vtkDataSet *_dataSet)
125 {
126  return new GaussCubature(_dataSet);
127 }
128 
129 GaussCubature *GaussCubature::Create(vtkDataSet *_dataSet,
130  const std::vector<int> &arrayIDs)
131 {
132  return new GaussCubature(_dataSet, arrayIDs);
133 }
134 
135 std::unique_ptr<GaussCubature> GaussCubature::CreateUnique(vtkDataSet *_dataSet)
136 {
137  return std::unique_ptr<GaussCubature>(GaussCubature::Create(_dataSet));
138 }
139 
140 std::unique_ptr<GaussCubature>
141 GaussCubature::CreateUnique(vtkDataSet *_dataSet,
142  const std::vector<int> &arrayIDs)
143 {
144  return std::unique_ptr<GaussCubature>(
145  GaussCubature::Create(_dataSet, arrayIDs));
146 }
147 
148 std::shared_ptr<GaussCubature>
149 GaussCubature::CreateShared(vtkDataSet *_dataSet)
150 {
151  std::shared_ptr<GaussCubature> cuby;
152  cuby.reset(GaussCubature::Create(_dataSet));
153  return cuby;
154 }
155 
156 std::shared_ptr<GaussCubature>
157 GaussCubature::CreateShared(vtkDataSet *_dataSet,
158  const std::vector<int> &arrayIDs)
159 {
160  std::shared_ptr<GaussCubature> cuby;
161  cuby.reset(GaussCubature::Create(_dataSet, arrayIDs));
162  return cuby;
163 }
164 
165 
167 {
168  // check whether arrayIDs exist in mesh
169  {
170  vtkSmartPointer<vtkPointData> pd = dataSet->GetPointData();
171  int numArr = pd->GetNumberOfArrays();
172  for (int arrayID : arrayIDs)
173  {
174  if (arrayID >= numArr)
175  {
176  std::cerr << "Array ID " << arrayID << " not found in dataset"
177  << std::endl;
178  exit(1);
179  }
180  }
181  }
182 
183  // Get the dictionary key
184  auto key = vtkQuadratureSchemeDefinition::DICTIONARY();
185 
186  // Get the cell types used by the data set
187  auto cellTypes = vtkSmartPointer<vtkCellTypes>::New();
188  dataSet->GetCellTypes(cellTypes);
189  int nCellTypes = cellTypes->GetNumberOfTypes();
190 
191  // create offset array and store the dictionary within
192  auto offsets = vtkSmartPointer<vtkIdTypeArray>::New();
193  std::string basename = "QuadratureOffset";
194  offsets->SetName(basename.c_str());
196  info = offsets->GetInformation();
197 
198  for (int typeId = 0; typeId < nCellTypes; ++typeId)
199  {
200  int cellType = cellTypes->GetCellType(typeId);
201  // Initialize quadrature scheme definition for given cell type
203  switch (cellType)
204  {
205  case VTK_TRIANGLE:
206  def->Initialize(VTK_TRIANGLE, 3, 3, TRI3, TRI3W);
207  break;
208  case VTK_TETRA:
209  def->Initialize(VTK_TETRA, 4, 4, TET4, TET4W);
210  break;
211  case VTK_HEXAHEDRON:
212  def->Initialize(VTK_HEXAHEDRON, 8, 8, HEX8, HEX8W);
213  break;
214  default:
215  std::cerr << "Error: Cell type: " << cellType << " found "
216  << "with no quadrature definition provided" << std::endl;
217  exit(1);
218  }
219  // the definition must appear in the dictionary associated with
220  // the offset array
221  key->Set(info, def, cellType);
222  }
223  // get dictionary size
224  int dictSize = key->Size(info);
225  dict = new vtkQuadratureSchemeDefinition *[dictSize];
226  key->GetRange(info, dict, 0, 0, dictSize);
227  vtkIdType numCells = dataSet->GetNumberOfCells();
228 
229  offsets->SetNumberOfTuples(numCells);
230 
231 #ifdef HAVE_OPENMP
232  std::vector<vtkIdType> chunkOffsets(omp_get_max_threads() + 1, 0);
233  #pragma omp parallel default(none) shared(numCells, chunkOffsets, offsets)
234  {
235  vtkIdType subOffset = 0;
236  auto genCell = vtkSmartPointer<vtkGenericCell>::New();
237 
238  #pragma omp single
239  {
240  // GetCell method is thread safe when first called from single thread
241  dataSet->GetCell(0, genCell);
242  }
243 
244  #pragma omp for schedule(static)
245  for (vtkIdType cellId = 0; cellId < numCells; ++cellId) {
246  offsets->SetValue(cellId, subOffset);
247 
248  dataSet->GetCell(cellId, genCell);
249  int cellType = genCell->GetCellType();
250  if (cellType >= VTK_TETRA) numVolCells += 1;
251 
252  vtkQuadratureSchemeDefinition *celldef = dict[cellType];
253  subOffset += celldef->GetNumberOfQuadraturePoints();
254  }
255 
256  int tid = omp_get_thread_num();
257  chunkOffsets[tid + 1] = subOffset;
258  #pragma omp barrier
259  #pragma omp single
260  {
261  for (int i = 1; i < chunkOffsets.size(); ++i) {
262  chunkOffsets[i] = chunkOffsets[i] + chunkOffsets[i - 1];
263  }
264  }
265 
266  #pragma omp for schedule(static)
267  for (vtkIdType cellId = 0; cellId < numCells; ++cellId) {
268  vtkIdType offset = chunkOffsets[tid] + offsets->GetValue(cellId);
269  offsets->SetValue(cellId, offset);
270  }
271  }
272 #else
273  vtkIdType offset = 0;
274 
275  for (int cellid = 0; cellid < numCells; ++cellid) {
276  offsets->SetValue(cellid, offset);
277  int cellType = dataSet->GetCell(cellid)->GetCellType();
278  if (cellType >= VTK_TETRA) {
279  numVolCells += 1;
280  }
281  vtkQuadratureSchemeDefinition *celldef = dict[cellType];
282  offset += celldef->GetNumberOfQuadraturePoints();
283  }
284 #endif
285 
286  dataSet->GetCellData()->AddArray(offsets);
287 
289 
290  pointGen->SetInputArrayToProcess
291  (0, 0, 0,
292  vtkDataObject::FIELD_ASSOCIATION_CELLS,
293  "QuadratureOffset");
294  pointGen->SetInputData(dataSet);
296  gaussMesh = vtkPolyData::SafeDownCast(pointGen->GetOutput());
297  pointGen->Update();
298 }
299 
300 
301 double GaussCubature::computeCellVolume(vtkSmartPointer<vtkGenericCell> genCell,
302  int cellType) const
303 {
304  switch (cellType)
305  {
306  case VTK_TRIANGLE:
307  {
308  return vtkMeshQuality::TriangleArea(genCell);
309  }
310  case VTK_QUAD:
311  {
312  return vtkMeshQuality::QuadArea(genCell);
313  }
314  case VTK_TETRA:
315  {
316  return vtkMeshQuality::TetVolume(genCell);
317  }
318  case VTK_HEXAHEDRON:
319  {
320  return vtkMeshQuality::HexVolume(genCell);
321  }
322  default:
323  {
324  std::cerr << "Error: Cell type: " << cellType << "found "
325  << "with no quadrature definition provided" << std::endl;
326  exit(1);
327  }
328  }
329 }
330 
331 
332 // FIXME: Can remove switch if you just use the general method implemented in tetra's case
333 double GaussCubature::computeJacobian(vtkSmartPointer<vtkGenericCell> genCell,
334  int cellType) const
335 {
336  switch (cellType)
337  {
338  case VTK_TRIANGLE:
339  {
340  return vtkMeshQuality::TriangleArea(genCell);
341  }
342  case VTK_QUAD:
343  {
344  return vtkMeshQuality::QuadArea(genCell) / 4.0;
345  }
346  case VTK_TETRA:
347  {
348  return vtkMeshQuality::TetVolume(genCell);
349  }
350  case VTK_HEXAHEDRON:
351  {
352  return vtkMeshQuality::HexVolume(genCell) / 8.0;
353  }
354  default:
355  {
356  std::cerr << "Error: Cell type: " << cellType << "found "
357  << "with no quadrature definition provided" << std::endl;
358  exit(1);
359  }
360  }
361 }
362 
363 
364 int GaussCubature::getOffset(int cellID) const
365 {
366  vtkIdType offsets[1];
367  vtkIdTypeArray::FastDownCast(
368  dataSet->GetCellData()->GetArray("QuadratureOffset"))
369  ->GetTypedTuple(cellID, offsets);
370  return offsets[0];
371 }
372 
373 
375 {
376  if (arrayIDs.empty())
377  {
378  std::cerr << "no array have been selected for interpolation" << std::endl;
379  exit(1);
380  }
381 
382  int numDataArr = gaussMesh->GetPointData()->GetNumberOfArrays();
383 
384  if (numDataArr == 0)
385  {
387  }
388 
389  // get number of gauss points in cell from dictionary
390  int numGaussPoints = dict[dataSet->GetCell(
391  cellID)->GetCellType()]
392  ->GetNumberOfQuadraturePoints();
393  // get offset from nodeMesh for lookup of gauss points in polyData
394  int offset = getOffset(cellID);
395 
396  //pntDataPairVec container;
397  pntDataPairVec container(numGaussPoints);
398 
399  vtkSmartPointer<vtkPointData> pd = gaussMesh->GetPointData();
400  for (int i = 0; i < numGaussPoints; ++i)
401  {
402  double x_tmp[3];
403  gaussMesh->GetPoint(offset + i, x_tmp);
404  std::vector<double> gaussPnt(x_tmp, x_tmp + 3);
405  std::vector<double> data(totalComponents);
406  int currcomp = 0;
407  for (int j = 0; j < numComponents.size(); ++j)
408  {
409  double *comps = new double[numComponents[j]];
410  pd->GetArray(j)->GetTuple(offset + i, comps);
411  for (int k = 0; k < numComponents[j]; ++k)
412  {
413  data[currcomp] = comps[k];
414  ++currcomp;
415  }
416  delete[] comps;
417  }
418  container[i] = std::make_pair(gaussPnt, data);
419  }
420  return container;
421 }
422 
423 
425  (const int cellID,
426  vtkSmartPointer<vtkGenericCell> genCell,
427  const std::vector<vtkSmartPointer<vtkDataArray>> &das,
428  std::vector<vtkSmartPointer<vtkDoubleArray>> &daGausses) const
429 {
430  // putting current cell into genCell
431  dataSet->GetCell(cellID, genCell);
432  // getting cellType information for lookup in map
433  int cellType = dataSet->GetCellType(cellID);
434  // get quadrature weights for this cell type
435  const double *shapeFunctionWeights = dict[cellType]->GetShapeFunctionWeights();
436  // number of gauss points in this cell
437  int numGaussPoints = dict[cellType]->GetNumberOfQuadraturePoints();
438  // get offset from nodeMesh for lookup of gauss points in polyData
439  int offset = getOffset(cellID);
440  // interpolation loop
441  for (int j = 0; j < numGaussPoints; ++j)
442  {
443  for (int id = 0; id < das.size(); ++id)
444  {
445  int numComponent = das[id]->GetNumberOfComponents();
446  auto *comps = new double[numComponent];
447  std::vector<double> interps(numComponent, 0.0);
448  for (int m = 0; m < genCell->GetNumberOfPoints(); ++m)
449  {
450  int pntId = genCell->GetPointId(m);
451  das[id]->GetTuple(pntId, comps);
452  for (int h = 0; h < numComponent; ++h)
453  {
454  interps[h] += comps[h] *
455  shapeFunctionWeights[j * genCell->GetNumberOfPoints() + m];
456  }
457  }
458  delete[] comps;
459  // adding interpolated value to data of cell
460  daGausses[id]->SetTuple(j + offset, interps.data());
461  }
462  }
463  return numGaussPoints;
464 }
465 
466 
468 {
469  if (arrayIDs.empty())
470  {
471  std::cerr << "no arrays selected for interpolation" << std::endl;
472  exit(1);
473  }
474 
475  std::vector<vtkSmartPointer<vtkDoubleArray>> daGausses(arrayIDs.size());
476  std::vector<vtkSmartPointer<vtkDataArray>> das(arrayIDs.size());
477  numComponents.resize(arrayIDs.size());
478  // initializing arrays storing interpolated data
479  for (int id = 0; id < arrayIDs.size(); ++id)
480  {
481  // get desired point data array to be interpolated to gauss points
482  vtkSmartPointer<vtkDataArray> da = dataSet->GetPointData()->GetArray(
483  arrayIDs[id]);
484  // get tuple length of given data
485  int numComponent = da->GetNumberOfComponents();
486  // declare data array to be populated with values at gauss points
487  vtkSmartPointer<vtkDoubleArray> daGauss = vtkSmartPointer<vtkDoubleArray>::New();
488  // names and sizing
489  daGauss->SetName(
490  dataSet->GetPointData()->GetArrayName(arrayIDs[id]));
491  daGauss->SetNumberOfComponents(numComponent);
492  daGauss->SetNumberOfTuples(gaussMesh->GetNumberOfPoints());
493  das[id] = da;
494  daGausses[id] = daGauss;
495  numComponents[id] = numComponent;
496  totalComponents += numComponent;
497  }
498  int numCells = dataSet->GetNumberOfCells();
499 #ifdef HAVE_OPENMP
500  #pragma omp parallel default(none) shared(numCells, das, daGausses)
501  {
502  vtkSmartPointer<vtkGenericCell> genCell =
504  #pragma omp single
505  {
506  // GetCell method is thread safe when first called from single thread
507  dataSet->GetCell(0, genCell);
508  }
509  #pragma omp for schedule(static)
510  for (int i = 0; i < numCells; ++i) {
511  interpolateToGaussPointsAtCell(i, genCell, das, daGausses);
512  }
513  }
514 #else
515  // generic cell to store given cell in dataSet
516  vtkSmartPointer<vtkGenericCell> genCell = vtkSmartPointer<vtkGenericCell>::New();
517  for (int i = 0; i < dataSet->GetNumberOfCells(); ++i)
518  {
519  interpolateToGaussPointsAtCell(i, genCell, das, daGausses);
520  }
521 #endif
522  for (int id = 0; id < arrayIDs.size(); ++id)
523  {
524  gaussMesh->GetPointData()->AddArray(daGausses[id]);
525  }
526 }
527 
528 
530  const std::vector<std::string> &newArrayNames) {
531  if (newArrayNames.empty()) {
532  std::cerr << "no arrays selected for interpolation" << std::endl;
533  exit(1);
534  }
535 
536  std::vector<vtkSmartPointer<vtkDoubleArray>> daGausses(newArrayNames.size());
537  std::vector<vtkSmartPointer<vtkDataArray>> das(newArrayNames.size());
538  // initializing arrays storing interpolated data
539  for (int id = 0; id < newArrayNames.size(); ++id) {
540  // get desired point data array to be interpolated to gauss points
541  vtkSmartPointer<vtkDataArray> da =
542  dataSet->GetPointData()->GetArray(&(newArrayNames[id])[0u]);
543  // get tuple length of given data
544  int numComponent = da->GetNumberOfComponents();
545  // declare data array to be populated with values at gauss points
546  vtkSmartPointer<vtkDoubleArray> daGauss = vtkSmartPointer<vtkDoubleArray>::New();
547  // names and sizing
548  daGauss->SetName(&(newArrayNames[id])[0u]);
549  daGauss->SetNumberOfComponents(numComponent);
550  daGauss->SetNumberOfTuples(gaussMesh->GetNumberOfPoints());
551  das[id] = da;
552  daGausses[id] = daGauss;
553  }
554  int numCells = dataSet->GetNumberOfCells();
555 #ifdef HAVE_OPENMP
556  #pragma omp parallel default(none) shared(numCells, das, daGausses)
557  {
558  // generic cell to store given cell in dataSet
559  vtkSmartPointer<vtkGenericCell> genCell =
561  #pragma omp single
562  {
563  // GetCell method is thread safe when first called from single thread
564  dataSet->GetCell(0, genCell);
565  }
566  #pragma omp for schedule(static)
567  for (int i = 0; i < numCells; ++i) {
568  interpolateToGaussPointsAtCell(i, genCell, das, daGausses);
569  }
570  }
571 #else
572  // generic cell to store given cell in dataSet
573  vtkSmartPointer<vtkGenericCell> genCell =
575  for (int i = 0; i < dataSet->GetNumberOfCells(); ++i) {
576  interpolateToGaussPointsAtCell(i, genCell, das, daGausses);
577  }
578 #endif
579  for (int id = 0; id < arrayIDs.size(); ++id)
580  {
581  gaussMesh->GetPointData()->AddArray(daGausses[id]);
582  }
583 }
584 
586  (int cellID,
587  vtkSmartPointer<vtkGenericCell> genCell,
588  vtkSmartPointer<vtkPointData> pd,
589  std::vector<vtkSmartPointer<vtkDoubleArray>> &integralData,
590  std::vector<std::vector<double>> &totalIntegralData) const
591 {
592  // putting cell from nodeMesh into genCell
593  dataSet->GetCell(cellID, genCell);
594  // getting cellType for looking up numGaussPoints in dictionary
595  // as well as computing scaled Jacobian
596  int cellType = genCell->GetCellType();
597 
598  // get number of gauss points in cell from dictionary
599  int numGaussPoints;
600  const double* quadWeights;
601  numGaussPoints = dict[cellType]->GetNumberOfQuadraturePoints();
602  quadWeights = dict[cellType]->GetQuadratureWeights();
603 
604  // computing Jacobian for integration
605  double jacobian = computeJacobian(genCell, cellType);
606  // get quadrature weights for this cell type
607  // get offset from nodeMesh for lookup of gauss points in polyData
608  int offset = getOffset(cellID);
609  // holds integrated data for each array
610  std::vector<std::vector<double>> data(arrayIDs.size());
611  // integration loop
612  for (int j = 0; j < integralData.size(); ++j) {
613  int numComponent = integralData[j]->GetNumberOfComponents();
614  data[j].resize(numComponent, 0.0);
615  auto comps = new double[numComponent];
616  for (int i = 0; i < numGaussPoints; ++i) {
617  pd->GetArray(j)->GetTuple(offset + i, comps);
618  for (int k = 0; k < numComponent; ++k) {
619  // TODO: generalize to support surface integration
620  if (genCell->GetCellDimension() == 3) {
621  data[j][k] += comps[k] * quadWeights[i];//*jacobian;
622  } else
623  data[j][k] += 0.0;
624  }
625  }
626  delete[] comps;
627  for (int k = 0; k < numComponent; ++k) {
628  data[j][k] *= jacobian;
629  totalIntegralData[j][k] += data[j][k];
630  }
631  // adding integrated value to data of cell
632  integralData[j]->SetTuple(cellID, data[j].data());
633  }
634 }
635 
636 
638  (int cellID,
639  vtkSmartPointer<vtkGenericCell> genCell,
640  vtkSmartPointer<vtkPointData> pd,
641  std::vector<vtkSmartPointer<vtkDoubleArray>> &integralData,
642  std::vector<std::vector<double>> &totalIntegralData,
643  const std::vector<std::string> &newArrayNames,
644  bool computeRMSE) const
645 {
646  // putting cell from nodeMesh into genCell
647  dataSet->GetCell(cellID, genCell);
648  // getting cellType for looking up numGaussPoints in dictionary
649  int cellType = dataSet->GetCell(cellID)->GetCellType();
650  // get number of gauss points in cell from dictionary
651  int numGaussPoints = dict[cellType]->GetNumberOfQuadraturePoints();
652  // computing Jacobian for integration
653  double jacobian = computeJacobian(genCell, cellType);
654  // computing volume for RMSE
655  double volume = computeCellVolume(genCell, cellType);
656  // get quadrature weights for this cell type
657  const double *quadWeights = dict[cellType]->GetQuadratureWeights();
658  // get offset from nodeMesh for lookup of gauss points in polyData
659  int offset = getOffset(cellID);
660  // holds integrated data for each array
661  std::vector<std::vector<double>> data(arrayIDs.size());
662  // integration loop
663  for (int j = 0; j < integralData.size(); ++j)
664  {
665  int numComponent = integralData[j]->GetNumberOfComponents();
666  data[j].resize(numComponent);
667  auto *comps = new double[numComponent];
668  for (int i = 0; i < numGaussPoints; ++i)
669  {
670  pd->GetArray(&(newArrayNames[j])[0u])->GetTuple(offset + i, comps);
671  for (int k = 0; k < numComponent; ++k)
672  {
673  // TODO: generalize to support surface integration
674  if (genCell->GetCellDimension() == 3)
675  {
676  data[j][k] += comps[k] * quadWeights[i];
677  }
678  else
679  data[j][k] += 0.0;
680  }
681  }
682  delete[] comps;
683  // taking sqrt of integrated value (RMSE)
684  for (int k = 0; k < numComponent; ++k)
685  {
686  data[j][k] *= jacobian;
687  data[j][k] = (computeRMSE ? std::sqrt(data[j][k] / volume) : data[j][k]);
688  //if (computeRMSE)
689  // data[j][k] = std::sqrt(data[j][k] / volume);
690  totalIntegralData[j][k] += data[j][k];
691  }
692  // adding integrated value to data of cell
693  integralData[j]->SetTuple(cellID, data[j].data());
694  }
695 }
696 
697 std::vector<std::vector<double>> GaussCubature::integrateOverAllCells()
698 {
699  int numCells = dataSet->GetNumberOfCells();
700 
701  if (gaussMesh->GetPointData()->GetNumberOfArrays() == 0)
702  {
704  }
705 
706  vtkSmartPointer<vtkPointData> pd = gaussMesh->GetPointData();
707  std::vector<vtkSmartPointer<vtkDoubleArray>> cellIntegralData(arrayIDs.size());
708  std::vector<std::vector<double>> totalIntegralData(arrayIDs.size());
709  for (int id = 0; id < arrayIDs.size(); ++id)
710  {
711  std::string arrName(
712  dataSet->GetPointData()->GetArrayName(arrayIDs[id]));
713  arrName.append("Integral");
714  vtkSmartPointer<vtkDoubleArray> integralDatum = vtkSmartPointer<vtkDoubleArray>::New();
715  integralDatum->SetName(&arrName[0u]);
716  integralDatum->SetNumberOfComponents(numComponents[id]);
717  integralDatum->SetNumberOfTuples(dataSet->GetNumberOfCells());
718  cellIntegralData[id] = integralDatum;
719  totalIntegralData[id].resize(numComponents[id], 0);
720  }
721 
722 #ifdef HAVE_OPENMP
723  #pragma omp parallel default(none) shared(numCells, pd, cellIntegralData, totalIntegralData, cerr)
724  {
725  auto genCell = vtkSmartPointer<vtkGenericCell>::New();
726 
727  #pragma omp single
728  {
729  // GetCell method is thread safe when first called from single thread
730  dataSet->GetCell(0, genCell);
731  }
732  auto partialIntegralData = totalIntegralData;
733 
734  #pragma omp for schedule(static)
735  for (int i = 0; i < numCells; ++i) {
736  integrateOverCell(i, genCell, pd, cellIntegralData, partialIntegralData);
737  }
738 
739  #pragma omp critical
740  {
741  // total integral data has same dimensions as cell integral data ...
742  // sum partial integrals
743  for (int id = 0; id < partialIntegralData.size(); ++id) {
744  for (int k = 0; k < partialIntegralData[id].size(); ++k) {
745  totalIntegralData[id][k] += partialIntegralData[id][k];
746  }
747  }
748  }
749  }
750 #else
751  auto genCell = vtkSmartPointer<vtkGenericCell>::New();
752  for (int i = 0; i < numCells; ++i) {
753  integrateOverCell(i, genCell, pd, cellIntegralData, totalIntegralData);
754  }
755 #endif
756 
757  for (int id = 0; id < arrayIDs.size(); ++id) {
758  dataSet->GetCellData()->AddArray(cellIntegralData[id]);
759  }
760  return totalIntegralData;
761 }
762 
763 
764 std::vector<std::vector<double>>
766  const std::vector<std::string> &newArrayNames,
767  bool computeRMSE)
768 {
769  interpolateToGaussPoints(newArrayNames);
770 
771  vtkSmartPointer<vtkPointData> pd = gaussMesh->GetPointData();
772  std::vector<vtkSmartPointer<vtkDoubleArray>> cellIntegralData(newArrayNames.size());
773  std::vector<std::vector<double>> totalIntegralData(newArrayNames.size());
774  for (int id = 0; id < newArrayNames.size(); ++id)
775  {
776  std::string name = newArrayNames[id] + "Integral";
777  vtkSmartPointer<vtkDoubleArray> integralDatum = vtkSmartPointer<vtkDoubleArray>::New();
778  integralDatum->SetName(&name[0u]);
779  int numComponent = pd->GetArray(
780  &(newArrayNames[id])[0u])->GetNumberOfComponents();
781  integralDatum->SetNumberOfComponents(numComponent);
782  integralDatum->SetNumberOfTuples(dataSet->GetNumberOfCells());
783  cellIntegralData[id] = integralDatum;
784  totalIntegralData[id].resize(numComponent, 0);
785  }
786 
787  int numCells = dataSet->GetNumberOfCells();
788 
789 #ifdef HAVE_OPENMP
790  #pragma omp parallel default(none) \
791  shared(numCells, pd, cellIntegralData, totalIntegralData, newArrayNames, computeRMSE)
792  {
793  auto genCell = vtkSmartPointer<vtkGenericCell>::New();
794  #pragma omp single
795  {
796  // GetCell method is thread safe when first called from single thread
797  dataSet->GetCell(0, genCell);
798  }
799  auto partialIntegralData = totalIntegralData;
800  #pragma omp for schedule(static)
801  for (int i = 0; i < numCells; ++i) {
802  integrateOverCell(i, genCell, pd, cellIntegralData, partialIntegralData,
803  newArrayNames, computeRMSE);
804  }
805  #pragma omp critical
806  {
807  // total integral data has same dimensions as cell integral data ...
808  // sum partial integrals
809  for(int id = 0; id < partialIntegralData.size(); ++id) {
810  for(int k = 0; k < partialIntegralData[id].size(); ++k) {
811  totalIntegralData[id][k] += partialIntegralData[id][k];
812  }
813  }
814  }
815  }
816 #else
817  vtkSmartPointer<vtkGenericCell> genCell = vtkSmartPointer<vtkGenericCell>::New();
818  for (int i = 0; i < numCells; ++i) {
819  integrateOverCell(i, genCell, pd, cellIntegralData, totalIntegralData,
820  newArrayNames, computeRMSE);
821  }
822 #endif
823 
824  for (int id = 0; id < newArrayNames.size(); ++id)
825  {
826  dataSet->GetCellData()->AddArray(cellIntegralData[id]);
827  }
828  return totalIntegralData;
829 }
830 
831 
832 void GaussCubature::writeGaussMesh(const char *name) const
833 {
834  if (gaussMesh)
835  writeVTFile<vtkXMLPolyDataWriter>(name, gaussMesh);
836  else
837  {
838  std::cerr << "Gauss point mesh has not been constructed" << std::endl;
839  exit(1);
840  }
841 }
double TET4W[]
Definition: Cubature.C:77
double computeJacobian(vtkSmartPointer< vtkGenericCell > genCell, int cellType) const
Definition: Cubature.C:333
data_type data
Edge/face with sorted point ids (a, b, c, ...) is located at some index i in data[b], with data[b][i].first == [a, c] (for edges, third point id treated as -1).
std::vector< int > numComponents
Definition: Cubature.H:146
void writeGaussMesh(const char *name) const
Definition: Cubature.C:832
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
double TET4[]
Definition: Cubature.C:68
std::vector< pntDataPair > pntDataPairVec
Definition: Cubature.H:55
void interpolateToGaussPoints()
Interpolate values to gauss points for arrays specified in arrayIDs at construction time...
Definition: Cubature.C:467
std::vector< std::vector< double > > integrateOverAllCells()
Integrate arrays specified by arrayIDs over all cells.
Definition: Cubature.C:697
static GaussCubature * Create(vtkDataSet *_dataSet)
Definition: Cubature.C:124
double computeCellVolume(vtkSmartPointer< vtkGenericCell > genCell, int cellType) const
Definition: Cubature.C:301
void constructGaussMesh()
Definition: Cubature.C:166
GaussCubature(vtkDataSet *_dataSet)
Definition: Cubature.C:107
double HEX8[]
Definition: Cubature.C:79
static std::shared_ptr< GaussCubature > CreateShared(vtkDataSet *_dataSet)
Definition: Cubature.C:149
int totalComponents
Definition: Cubature.H:147
vtkIdType id
id in .inp file
Definition: inpGeoMesh.C:128
double HEX8W[]
Definition: Cubature.C:104
VTKCellType cellType
Definition: inpGeoMesh.C:129
int interpolateToGaussPointsAtCell(int cellID, vtkSmartPointer< vtkGenericCell > genCell, const std::vector< vtkSmartPointer< vtkDataArray >> &das, std::vector< vtkSmartPointer< vtkDoubleArray >> &daGausses) const
Definition: Cubature.C:425
int getOffset(int cellID) const
Definition: Cubature.C:364
vtkSmartPointer< vtkDataSet > dataSet
Definition: Cubature.H:137
vtkSmartPointer< vtkPolyData > gaussMesh
Definition: Cubature.H:141
pntDataPairVec getGaussPointsAndDataAtCell(int cellID)
Returns coordinates of gauss points and associated data at cell.
Definition: Cubature.C:374
double TRI3[]
Definition: Cubature.C:58
double TRI3W[]
Definition: Cubature.C:65
std::vector< int > arrayIDs
Definition: Cubature.H:145
void integrateOverCell(int cellID, vtkSmartPointer< vtkGenericCell > genCell, vtkSmartPointer< vtkPointData > pd, std::vector< vtkSmartPointer< vtkDoubleArray >> &integralData, std::vector< std::vector< double >> &totalIntegralData) const
Definition: Cubature.C:586
int numVolCells
Definition: Cubature.H:139
vtkQuadratureSchemeDefinition ** dict
Definition: Cubature.H:143
static std::unique_ptr< GaussCubature > CreateUnique(vtkDataSet *_dataSet)
Definition: Cubature.C:135