rsvs3D  0.0.0
Codes for the c++ implementation of the 3D RSVS
tetgenrsvs.cpp
1 #include "tetgenrsvs.hpp"
2 
3 #include <array>
4 #include <cmath>
5 #include <fstream>
6 #include <iostream>
7 #include <unordered_map>
8 #include <vector>
9 
10 #include "arraystructures.hpp"
11 #include "mesh.hpp"
12 #include "meshprocessing.hpp"
13 #include "meshrefinement.hpp"
14 #include "postprocessing.hpp"
15 #include "rsvsjson.hpp"
16 #include "snake.hpp"
17 #include "tetgen.h"
18 #include "triangulate.hpp"
19 #include "voxel.hpp"
20 #include "warning.hpp"
21 
22 using namespace std;
23 using json = rsvsjson::json;
24 
25 void tetgen::internal::MeshData2Tetgenio(const mesh &meshgeom, tetgen::io_safe &tetin, int facetOffset, int pointOffset,
26  int pointMarker, const std::vector<double> &pointMtrList,
27  const std::vector<double> &facetConstr, int facetConstrOffset)
28 {
29  /*
30  Writes meshdata into the tetgenio format for a single mesh
31  */
32  vector<int> orderVert;
33  int countI, countJ, countK, nFacetConstr;
34 
35  // Set point properties to the appropriate lists
36  countI = meshgeom.verts.size();
37  countJ = min(int(pointMtrList.size()), tetin.numberofpointmtrs);
38  for (int i = 0; i < countI; ++i)
39  {
40  // set point coordinates
41  for (int j = 0; j < 3; ++j)
42  {
43  tetin.pointlist[(i + pointOffset) * 3 + j] = meshgeom.verts(i)->coord[j];
44  }
45  // set point metrics
46  for (int j = 0; j < countJ; ++j)
47  {
48  tetin.pointmtrlist[(i + pointOffset) * countJ + j] = pointMtrList[j];
49  }
50  // set point markers
51  tetin.pointmarkerlist[i + pointOffset] = pointMarker;
52  }
53 
54  // Set facet properties to the appropriate lists
55  nFacetConstr = facetConstr.size();
56  // std::cout << "Number of facetconstraint " << nFacetConstr << std::endl;
57  for (int i = 0; i < nFacetConstr; ++i)
58  {
59  // set facet constraints
60  tetin.facetconstraintlist[(facetConstrOffset + i) * 2] = double(facetConstrOffset + i + 1);
61  tetin.facetconstraintlist[(facetConstrOffset + i) * 2 + 1] = facetConstr[i];
62  }
63 
64  // Set connectivity through facet and polygon
65  countI = meshgeom.surfs.size();
66  if (nFacetConstr != 1 && nFacetConstr != countI && nFacetConstr != 0)
67  {
68  std::cerr << "Warning: Number of constraints is not 1 or the "
69  "number of surfaces, the code will work but the behaviour "
70  "is suprising."
71  << std::endl;
72  }
73  nFacetConstr = max(nFacetConstr, 1);
74  for (int i = 0; i < countI; ++i)
75  {
76  tetin.allocatefacet(i + facetOffset, 1);
77 
78  meshgeom.surfs(i)->OrderedVerts(&meshgeom, orderVert);
79  tetin.allocatefacetpolygon(i + facetOffset, 0, orderVert.size());
80  countK = orderVert.size();
81  for (int k = 0; k < countK; ++k)
82  {
83  tetin.facetlist[i + facetOffset].polygonlist[0].vertexlist[k] =
84  meshgeom.verts.find(orderVert[k]) + pointOffset;
85  }
86  // Set the facet marker list to match the mod of the facets added
87  tetin.facetmarkerlist[i + facetOffset] = facetConstrOffset + (i % nFacetConstr) + 1;
88  // pointMarker;//
89  }
90 }
91 
92 void tetgen::internal::Mesh2Tetgenio(const mesh &meshgeom, const mesh &meshdomain, tetgen::io_safe &tetin, int numHoles)
93 {
94  /*
95  Converts a mesh into the safe allocation tetgenio format.
96 
97  Rules of conversion are:
98  - surfaces become a facet
99  - edges are not logged
100  - points come as a list
101  */
102 
103  tetin.firstnumber = 0;
104 
105  tetin.numberoffacets = meshgeom.surfs.size() + meshdomain.surfs.size();
106  tetin.numberofholes = numHoles;
107  tetin.numberofpoints += meshgeom.verts.size() + meshdomain.verts.size();
108 
109  tetin.numberofpointmtrs = 1;
110  tetin.numberoffacetconstraints = 2;
111 
112  tetin.allocate();
113 
114  tetgen::internal::MeshData2Tetgenio(meshgeom, tetin, 0, 0, 1, {0.0}, {0.0}, 0);
115  tetgen::internal::MeshData2Tetgenio(meshdomain, tetin, meshgeom.surfs.size(), meshgeom.verts.size(), -1, {-1.0},
116  {0.0}, 1);
117 }
118 
119 void tetgen::internal::Mesh2TetgenioPoints(const mesh &meshgeom, const mesh &meshdomain, tetgen::io_safe &tetin)
120 {
121  /*
122  Converts a mesh into the safe allocation tetgenio format.
123 
124  Rules of conversion are:
125  - volu becomes a facet
126  - surf becomes a polygon in the facet
127  - edges are not logged
128  - points come as a list
129  */
130 
131  tetin.firstnumber = 0;
132 
133  tetin.numberoffacets = 0;
134  tetin.numberofholes = 0;
135  tetin.numberofpoints += meshgeom.verts.size() + meshdomain.verts.size();
136 
137  tetin.numberofpointmtrs = 0;
138  tetin.numberoffacetconstraints = 0;
139 
140  tetin.allocate();
141  int nPtsGeom = meshgeom.verts.size();
142  for (int i = 0; i < nPtsGeom; ++i)
143  {
144  for (int j = 0; j < 3; ++j)
145  {
146  tetin.pointlist[i * 3 + j] = meshgeom.verts(i)->coord[j];
147  }
148  }
149  int nPtsDom = meshdomain.verts.size();
150  for (int i = 0; i < nPtsDom; ++i)
151  {
152  for (int j = 0; j < 3; ++j)
153  {
154  tetin.pointlist[(i + nPtsGeom) * 3 + j] = meshdomain.verts(i)->coord[j];
155  }
156  }
157 }
158 
159 void tetgen::internal::PointCurvature2Metric(std::vector<double> &vertCurvature, const tetgen::apiparam &inparam)
160 {
161  double maxCurv = *max_element(vertCurvature.begin(), vertCurvature.end());
162  double minCurv = 0.0;
163  double deltaCurv = maxCurv - minCurv;
164  auto lininterp = [&](double curv) -> double {
165  return (inparam.surfedgelengths[1] - inparam.surfedgelengths[0]) / (deltaCurv) * (curv - minCurv) +
166  inparam.surfedgelengths[0];
167  };
168 
169  int count = vertCurvature.size();
170  for (int i = 0; i < count; ++i)
171  {
172  vertCurvature[i] = lininterp(vertCurvature[i]);
173  }
174 }
175 
176 void tetgen::input::RSVS2CFD(const snake &snakein, tetgen::io_safe &tetin, const tetgen::apiparam &tetgenParam)
177 {
178  /*
179  Processes a snake object into a tetgen input object.
180 
181  This function processes snake objects into the safe tetgenio object.
182  This can then used to generate a tetrahedral mesh or to save it as the
183  input file.
184  */
185 
186  mesh meshdomain, meshgeom;
187  triangulation triRSVS;
188  int nHoles;
189  // int nPtsHole, kk, count;
190 
191  std::vector<int> holeIndices;
192  std::array<double, 3> upperB, lowerB;
193  std::vector<int> vertPerSubDomain;
194  std::vector<double> holeCoords;
195 
196  PrepareSnakeForCFD(snakein, tetgenParam.distanceTol, meshgeom, holeCoords);
197 
198  meshgeom.ReturnBoundingBox(lowerB, upperB);
199  double distBound = tetgenParam.edgelengths.size() > 0 ? tetgenParam.edgelengths[0] : tetgenParam.distanceTol;
200  for (int i = 0; i < 3; ++i)
201  {
202  lowerB[i] -= distBound;
203  upperB[i] += distBound;
204  }
205  meshdomain = BuildCutCellDomain(tetgenParam.lowerB, tetgenParam.upperB, lowerB, upperB,
206  tetgenParam.edgelengths.size(), vertPerSubDomain);
207  nHoles = holeCoords.size() / 3;
208  tetgen::internal::Mesh2Tetgenio(meshgeom, meshdomain, tetin, nHoles);
209 
210  // std::cout<< std::endl << "Number of holes " << nHoles << std::endl;
211 
212  // Assign the holes
213 
214  int count = holeCoords.size();
215  for (int i = 0; i < count; ++i)
216  {
217  tetin.holelist[i] = holeCoords[i];
218  }
219 
220  // Assign domain point metrics
221  int startPnt = meshgeom.verts.size();
222  if (tetgenParam.surfedgelengths[0] == tetgenParam.surfedgelengths[1])
223  {
224  tetin.SpecifyTetPointMetric(0, startPnt, {tetgenParam.surfedgelengths[0]});
225  }
226  else
227  {
228  auto vertCurvature = CalculateVertexCurvature(meshgeom, tetgenParam.curvatureSmoothing);
229  DisplayVectorStatistics(vertCurvature);
230  tetgen::internal::PointCurvature2Metric(vertCurvature, tetgenParam);
231  tetin.SpecifyTetPointMetric(0, startPnt, vertCurvature);
232  DisplayVectorStatistics(vertCurvature);
233  }
234  for (int i = 0; i < int(tetgenParam.edgelengths.size()); ++i)
235  {
236  tetin.SpecifyTetPointMetric(startPnt, vertPerSubDomain[i], {tetgenParam.edgelengths[i]});
237  startPnt = startPnt + vertPerSubDomain[i];
238  }
239  // Remove facet markers from the internal domain faces
240  startPnt = meshgeom.surfs.size();
241  int nSurfPerSubDomain = meshdomain.surfs.size() / vertPerSubDomain.size();
242  for (int i = 0; i < int(vertPerSubDomain.size()) - 1; ++i)
243  {
244  tetin.SpecifyTetFacetMetric(startPnt, nSurfPerSubDomain, 0);
245  startPnt = startPnt + nSurfPerSubDomain;
246  }
247 }
248 
249 void tetgen::input::RSVSGRIDS(const mesh &meshdomain, const mesh &meshboundary, tetgen::io_safe &tetin,
250  const tetgen::apiparam &tetgenParam)
251 {
252  /*
253  Processes a snake object into a tetgen input object.
254 
255  This function processes snake objects into the safe tetgenio object.
256  This can then used to generate a tetrahedral mesh or to save it as the
257  input file.
258  */
259 
260  triangulation triRSVS;
261  int nHoles = 0;
262  // int nPtsHole, kk, count;
263 
264  std::vector<int> holeIndices;
265  std::vector<int> vertPerSubDomain;
266 
267  vertPerSubDomain.push_back(meshdomain.verts.size());
268 
269  tetgen::internal::Mesh2Tetgenio(meshboundary, meshdomain, tetin, nHoles);
270 
271  int startPnt = meshboundary.verts.size();
272  // Assign "boundary" point metrics
273  auto vertEdgeLength = CalculateVertexMaxEdgeLength(meshboundary);
274  int count = vertEdgeLength.size();
275  for (int i = 0; i < count; ++i)
276  {
277  vertEdgeLength[i] = vertEdgeLength[i] * tetgenParam.edgelengths[0];
278  }
279  tetin.SpecifyTetPointMetric(0, startPnt, vertEdgeLength);
280  // Assign domain point metrics
281  vertEdgeLength = CalculateVertexMaxEdgeLength(meshdomain);
282  count = vertEdgeLength.size();
283  for (int i = 0; i < count; ++i)
284  {
285  vertEdgeLength[i] = vertEdgeLength[i] * tetgenParam.edgelengths[0];
286  }
287  tetin.SpecifyTetPointMetric(startPnt, startPnt + vertEdgeLength.size(), vertEdgeLength);
288 }
289 
290 void tetgen::input::RSVSGRIDS(const mesh &meshdomain, tetgen::io_safe &tetin, const tetgen::apiparam &tetgenParam)
291 {
292  mesh meshboundary;
293  meshboundary.Init(0, 0, 0, 0);
294  meshboundary.HashArray();
295 
296  tetgen::input::RSVSGRIDS(meshdomain, meshboundary, tetin, tetgenParam);
297 }
298 
299 void tetgen::input::POINTGRIDS(const mesh &meshdomain, tetgen::io_safe &tetin, const tetgen::apiparam &tetgenParam,
300  bool generateVoroBound)
301 {
302  /*
303  Processes a snake object into a tetgen input object.
304 
305  This function processes snake objects into the safe tetgenio object.
306  This can then used to generate a tetrahedral mesh or to save it as the
307  input file.
308  */
309 
310  mesh meshgeom;
311  triangulation triRSVS;
312  // int nPtsHole, kk, count;
313  if (generateVoroBound)
314  {
315  // Generate a set of points which fulfill the following:
316  // - off the boundaries by dist tol using the "pinch" technique:
317  // not on the corners or edges but on the faces that need
318  // to be closed
319  // For each face place 1 point in the middle.
320  // For each edge place 2 points in the middle.
321  // For each face place 3 points on the outside.
322  int nVertsAdded = 0;
323  std::vector<double> vertsToAdd;
324  std::array<double, 3> vertModif = {0, 0, 0}, edgeModif = {0, 0, 0};
325 #ifdef RSVS_DIAGNOSTIC_RESOLVED
326  tecplotfile tecout;
327  tecout.OpenFile("../TESTOUT/rsvs_voro.plt", "a");
328 #endif
329  vertsToAdd.reserve(100);
330  // add the points associated with the vertices
331  for (int l = 0; l < 4; ++l)
332  {
333  vertModif = {0, 0, 0};
334  if (l < 3)
335  {
336  vertModif[l] = tetgenParam.distanceTol;
337  }
338  for (int i = 0; i < 8; ++i)
339  {
340  for (int j = 0; j < 3; ++j)
341  {
342  int k = pow(2, j);
343  double delta = (tetgenParam.upperB[j] - tetgenParam.lowerB[j]) * vertModif[j];
344  vertsToAdd.push_back(
345  (((i / k) % 2) ? tetgenParam.upperB[j] + delta : tetgenParam.lowerB[j] - delta));
346  }
347  nVertsAdded++;
348  }
349  }
350 
351  // add the points associated with the edges
352  int nEdgeSteps = ceil(1.0 / (tetgenParam.distanceTol * 2));
353  for (int ll = 1; ll < nEdgeSteps; ++ll)
354  {
355  for (int l = 0; l < 3; ++l)
356  { // for each direction of the edge (coord = 0.5)
357  edgeModif = {0, 0, 0};
358  edgeModif[l] = 1.0 / double(nEdgeSteps) * ll;
359  for (int j = 0; j < 2; ++j)
360  { // for each displacement off the edge
361  vertModif = {0, 0, 0};
362  vertModif[(l + j + 1) % 3] = tetgenParam.distanceTol;
363  for (int i = 0; i < 4; ++i)
364  { // each of the edge that make a square
365 
366  for (int m = 0; m < 3; ++m)
367  {
368  if (m == l)
369  { // this is the inactive dimension
370  vertsToAdd.push_back(edgeModif[l]);
371  }
372  else
373  {
374  double delta = (tetgenParam.upperB[m] - tetgenParam.lowerB[m]) * vertModif[m];
375  int k = pow(2, (m + 3 - (l + 1)) % 3);
376  vertsToAdd.push_back(
377  (((i / k) % 2) ? tetgenParam.upperB[m] + delta : tetgenParam.lowerB[m] - delta));
378  }
379  }
380  nVertsAdded++;
381  }
382  }
383  }
384  }
385  // add the points associated with the faces
386  // add the points into a mesh
387 
388  meshgeom = Points2Mesh(vertsToAdd, 3);
389 #ifdef RSVS_DIAGNOSTIC_RESOLVED
390  tecout.PrintMesh(meshgeom, 0, 0, rsvs3d::constants::tecplot::point);
391 #endif
392  }
393  else
394  {
395  meshgeom.Init(0, 0, 0, 0);
396  }
397  meshgeom.PrepareForUse();
398 
399  tetgen::internal::Mesh2TetgenioPoints(meshgeom, meshdomain, tetin);
400 }
401 
413 void tetgen::output::SU2(const char *fileName, const tetgenio &tetout)
414 {
415  std::ofstream meshFile;
416  std::vector<int> diffBounds, numBounds;
417  int DEINCR;
418 
419  meshFile.open(fileName);
420  // De-increments indices if tetgen indices start from 1 to match SU2
421  // which is 0 indexed
422  DEINCR = tetout.firstnumber ? -1 : 0;
423 
424  // If file was not opened correctly
425  if (meshFile.fail())
426  {
427  std::cerr << std::endl << "Error: " << fileName << std::endl << " in could not be opened" << std::endl;
428  RSVS3D_ERROR_ARGUMENT("Output mesh file could not be opened");
429  }
430  // Set appropriate precision for mesh point position
431  meshFile.precision(16);
432  // Dimensions
433  meshFile << "NDIME= 3" << std::endl;
434  // Points
435  meshFile << "NPOIN= " << tetout.numberofpoints << std::endl;
436  for (int i = 0; i < tetout.numberofpoints; ++i)
437  {
438  for (int j = 0; j < 3; ++j)
439  {
440  meshFile << tetout.pointlist[i * 3 + j] << " ";
441  }
442  meshFile << std::endl;
443  }
444  // Elements (edges, triangles and tets)
445  int nElms = tetout.numberoftetrahedra;
446  meshFile << "NELEM= " << nElms << std::endl;
447 
448  for (int i = 0; i < tetout.numberoftetrahedra; ++i)
449  { // Tetrahedra
450  meshFile << "10 ";
451  for (int j = 0; j < 4; ++j)
452  {
453  meshFile << tetout.tetrahedronlist[i * tetout.numberofcorners + j] + DEINCR << " ";
454  }
455  meshFile << std::endl;
456  }
457  // Boundaries
458  // 1 - Process number of different boundaries
459  diffBounds.reserve(10);
460  numBounds.reserve(10);
461  diffBounds.push_back(0);
462  numBounds.push_back(0);
463  int nBounds = diffBounds.size();
464  // Build an array of different boundary numbers.
465  // 0 is first as it is likely the most common
466  for (int i = 0; i < tetout.numberoftrifaces; ++i)
467  {
468  bool flagSame = false;
469  int j = 0;
470  while ((j < nBounds) && !flagSame)
471  {
472  flagSame = flagSame || (diffBounds[j] == tetout.trifacemarkerlist[i]);
473  ++j;
474  }
475  if (!flagSame)
476  {
477  diffBounds.push_back(tetout.trifacemarkerlist[i]);
478  numBounds.push_back(0);
479  ++nBounds;
480  ++j;
481  }
482  numBounds[j - 1]++;
483  }
484  // DisplayVector(diffBounds);
485  // DisplayVector(numBounds);
486  // 2 - write boundaries (skipping 0 boundary)
487  meshFile << "NMARK= " << nBounds - 1 << std::endl;
488  for (int k = 1; k < nBounds; ++k)
489  {
490  meshFile << "MARKER_TAG= " << diffBounds[k] << std::endl;
491  meshFile << "MARKER_ELEMS= " << numBounds[k] << std::endl;
492  for (int i = 0; i < tetout.numberoftrifaces; ++i)
493  { // Triangular faces
494  if (tetout.trifacemarkerlist[i] == diffBounds[k])
495  {
496  meshFile << "5 ";
497  for (int j = 0; j < 3; ++j)
498  {
499  meshFile << tetout.trifacelist[i * 3 + j] + DEINCR << " ";
500  }
501  meshFile << std::endl;
502  }
503  }
504  }
505 
506  // Edges not needed
507  // for (int i = 0; i < tetout.numberofedges; ++i)
508  // { // Edges
509  // meshFile << "3 " << tetout.edgelist[i*2]<< " "
510  // << tetout.edgelist[i*2+1] << " " << std::endl;
511  // }
512 }
513 
514 void tetgen::internal::CloseVoronoiMesh(mesh &meshout, tetgen::io_safe &tetout, std::vector<int> &rayEdges, int DEINCR,
515  tetgen::dombounds boundBox)
516 {
517  /*
518  Mesh is still missing some elements due to Voronoi diagrams not being
519  naturally closed:
520  1) Terminate rays with vertices
521  2.a) Link ray-vertices with edges
522  2.b) Include edges in equivalent faces
523  3.a) Close volumes by creating faces from edges
524  4) Triangulate the surface mesh to make sure it is flat
525  Implementation notes:
526  will need hashed vectors of the edges added
527 
528  Issue:
529  A voronoi is not bounded -> need another algorithm there
530  (idea -> points lying out of convex hull removed and the
531  tetout vertex becomes a ray)
532  */
533 
534  int nVerts, nEdges, nSurfs;
535  int count, n;
536  double lStep, lStepMin = -0.00;
537  vert vertNew;
538  edge edgeNew;
539  surf surfNew;
540  HashedVector<int, int> rays, newVerts, newEdges, newSurfs, rayFacesSearch;
541  std::vector<int> rayCells; // nonclosed voronoi faces
542  std::vector<int> &rayFaces = rayFacesSearch.vec; // nonclosed voronoi faces
543  std::vector<int> pair;
544 
545  nVerts = meshout.verts.size();
546  nEdges = meshout.edges.size();
547  nSurfs = meshout.surfs.size();
548 
549  rays.vec = rayEdges;
550  rays.GenerateHash();
551  // Terminating rays:
552  // Use a fixed length and assign connectivity to the ray
553  count = rayEdges.size();
554  vertNew.edgeind.reserve(6);
555  vertNew.edgeind.assign(1, 0);
556  for (int i = 0; i < count; ++i)
557  {
558  vertNew.index = nVerts + 1;
559  vertNew.edgeind[0] = rayEdges[i];
560  meshout.edges[rayEdges[i] - 1].vertind[1] = nVerts + 1;
561  lStep =
562  tetgen::internal::ProjectRay(3, boundBox, tetout.vedgelist[rayEdges[i] - 1].vnormal,
563  meshout.verts[tetout.vedgelist[rayEdges[i] - 1].v1 + DEINCR].coord, lStepMin);
564  // std::cout << lStep << " " << std::endl;
565  for (int j = 0; j < 3; ++j)
566  {
567  vertNew.coord[j] = lStep * tetout.vedgelist[rayEdges[i] - 1].vnormal[j] +
568  meshout.verts[tetout.vedgelist[rayEdges[i] - 1].v1 + DEINCR].coord[j];
569  }
570  meshout.verts.push_back(vertNew);
571  newVerts.vec.push_back(nVerts + 1);
572  nVerts++;
573  }
574 
575  newVerts.GenerateHash();
576 
577  // For each face
578  // check if it is linked to rays, if it is
579  // an edge will be established between the two ray vertices
580  meshout.edges.HashArray();
581  rayFaces = ConcatenateVectorField(meshout.edges, &edge::surfind, meshout.edges.find_list(rays.vec));
582  sort(rayFaces);
583  unique(rayFaces);
584  count = rayFaces.size();
585  edgeNew.surfind.reserve(6);
586  edgeNew.surfind.assign(1, 0);
587  for (int i = 0; i < count; ++i)
588  {
589  // identify in each open face the 2 rays
590  pair.clear();
591  for (int a : rays.find_list(meshout.surfs[rayFaces[i] - 1].edgeind))
592  {
593  if (a != -1)
594  {
595  pair.push_back(a);
596  }
597  }
598  if (pair.size() != 2)
599  {
600  RSVS3D_ERROR_LOGIC("Voronoi open face should have 2 rays");
601  }
602  // Connect the 2 rays and their edges
603  edgeNew.index = nEdges + 1;
604  for (int j = 0; j < 2; ++j)
605  {
606  edgeNew.vertind[j] = meshout.edges[rays.vec[pair[j]] - 1].vertind[1];
607  meshout.verts[edgeNew.vertind[j] - 1].edgeind.push_back(edgeNew.index);
608  }
609  edgeNew.surfind[0] = rayFaces[i];
610 
611  meshout.edges.push_back(edgeNew);
612  meshout.surfs[rayFaces[i] - 1].edgeind.push_back(edgeNew.index);
613  newEdges.vec.push_back(edgeNew.index);
614  nEdges++;
615  }
616 
617  newEdges.GenerateHash();
618  rayFacesSearch.GenerateHash();
619  // For each volume a surface is established closing
620  // the volume using all the face indices and looking
621  // for those which have been modified.
622 
623  meshout.surfs.HashArray();
624  rayCells = ConcatenateVectorField(meshout.surfs, &surf::voluind, meshout.surfs.find_list(rayFaces));
625  sort(rayCells);
626  unique(rayCells);
627  count = rayCells.size();
628 
629  for (int i = 0; i < count; ++i)
630  {
631  // identify in each open cell the faces that were open
632  pair.clear();
633  surfNew.edgeind.clear();
634  for (auto a : rayFacesSearch.find_list(meshout.volus[rayCells[i] - 1].surfind))
635  {
636  if (a != -1)
637  {
638  pair.push_back(a);
639  }
640  }
641  if (pair.size() < 3)
642  {
643  RSVS3D_ERROR_LOGIC("At least 3 edges are required to close the face");
644  }
645  // identify the edge in each face which is new and assign correxponding
646  // connectivities to surfNew;
647  surfNew.index = nSurfs + 1;
648  for (auto ind : pair)
649  {
650  auto &edgeind = meshout.surfs[rayFacesSearch.vec[ind] - 1].edgeind;
651  n = edgeind.size();
652  int j, k = 0;
653  for (j = 0; j < n; ++j)
654  {
655  if (newEdges.find(edgeind[j]) != -1)
656  {
657  surfNew.edgeind.push_back(edgeind[j]);
658  meshout.edges[edgeind[j] - 1].surfind.push_back(surfNew.index);
659  // break;
660  k++;
661  }
662  }
663  // if(j>=n)
664  if (k == 0)
665  {
666  RSVS3D_ERROR_LOGIC("None of the edges of the open face"
667  " were recognised as new. This should not happen.");
668  }
669  if (k > 1)
670  {
671  std::cerr << "Unexpected behaviour with number of edges" << std::endl;
672  }
673  }
674  surfNew.voluind[0] = rayCells[i];
675  surfNew.voluind[1] = 0;
676 
677  meshout.volus[rayCells[i] - 1].surfind.push_back(surfNew.index);
678  meshout.surfs.push_back(surfNew);
679  nSurfs++;
680  }
681 }
682 
683 tetgen::dombounds tetgen::output::GetBoundBox(tetgen::io_safe &tetout)
684 {
685  tetgen::dombounds domBounds;
686  domBounds[0] = {INFINITY, INFINITY, INFINITY};
687  domBounds[1] = {-INFINITY, -INFINITY, -INFINITY};
688  int nVerts = tetout.numberofpoints;
689  int count = nVerts;
690  int n = 3;
691  for (int i = 0; i < count; ++i)
692  {
693  for (int j = 0; j < n; ++j)
694  {
695  domBounds[0][j] =
696  domBounds[0][j] <= tetout.pointlist[i * n + j] ? domBounds[0][j] : tetout.pointlist[i * n + j];
697  domBounds[1][j] =
698  domBounds[1][j] >= tetout.pointlist[i * n + j] ? domBounds[1][j] : tetout.pointlist[i * n + j];
699  }
700  }
701 
702  return domBounds;
703 }
704 
705 mesh tetgen::output::VORO2MESH(tetgen::io_safe &tetout)
706 {
707  /*
708  Translates a tetgen output to the RSVS native mesh format
709  */
710  mesh meshout;
711 
712  int nVerts, nEdges, nSurfs, nVolus;
713  int count, INCR, DEINCR, n;
714  vector<int> rayInd, rayInd2;
715  tetgen::dombounds boundBox = tetgen::output::GetBoundBox(tetout);
716 
717  nVerts = tetout.numberofvpoints;
718  nEdges = tetout.numberofvedges;
719  nSurfs = tetout.numberofvfacets;
720  nVolus = tetout.numberofvcells;
721 
722  // INCR and DEINCR convert from tetgen array position to index and
723  // index to array position respectively
724  INCR = tetout.firstnumber ? 0 : 1;
725  DEINCR = tetout.firstnumber ? -1 : 0;
726 
727  // if `tetrahedralize` has not been run with the correct flags connectivity is
728  // unavailable, throw an error. (tests for flag -v)
729  if (tetout.vpointlist == NULL)
730  {
731  RSVS3D_ERROR_ARGUMENT("TET2MESH requires flag -v be passed to tetgen");
732  }
733 
734  meshout.Init(nVerts, nEdges, nSurfs, nVolus);
735  meshout.PopulateIndices();
736 
737  // These need to be cleared so that push_back works as expected
738  for (int i = 0; i < nEdges; ++i)
739  {
740  meshout.edges[i].vertind.clear();
741  }
742  rayInd.reserve(nEdges);
743 
744  // Assign coordinates
745  count = nVerts;
746  n = 3;
747  for (int i = 0; i < count; ++i)
748  {
749  meshout.verts[i].index = i + 1;
750  for (int j = 0; j < n; ++j)
751  {
752  meshout.verts[i].coord[j] = tetout.vpointlist[i * n + j];
753  }
754  }
755 
756  // assign edge-vertex connectivity, relies on the implied ordering and
757  // indexing of the output tetgenio object an of `PopulateIndices()`
758  count = nEdges;
759  for (int i = 0; i < count; ++i)
760  { // Assign edge to vertex connectivity
761  meshout.edges[i].vertind = {tetout.vedgelist[i].v1 + INCR, tetout.vedgelist[i].v2 + INCR};
762  // Assign equivalent vertex to edge connectivity
763  meshout.verts[tetout.vedgelist[i].v1 + DEINCR].edgeind.push_back(i + 1);
764 
765  if (tetout.vedgelist[i].v2 > -1)
766  {
767  // if it is not a ray add it to vertex v2
768  meshout.verts[tetout.vedgelist[i].v2 + DEINCR].edgeind.push_back(i + 1);
769  }
770  else
771  {
772  // if it is a ray save it to the list of rays
773  rayInd.push_back(i + 1);
774  }
775  }
776 
777  count = nSurfs;
778  int nRays = 0;
779  for (int i = 0; i < count; ++i)
780  { // loop through surfs
781  // Surfaces to edges
782  n = tetout.vfacetlist[i].elist[0];
783  for (int j = 0; j < n; ++j)
784  { // loop through corresponding connections
785  if (tetout.vfacetlist[i].elist[j + 1] > -1)
786  {
787  meshout.surfs[i].edgeind.push_back(tetout.vfacetlist[i].elist[j + 1] + INCR);
788  meshout.edges[tetout.vfacetlist[i].elist[j + 1] + DEINCR].surfind.push_back(i + 1);
789  }
790  else
791  {
792  nRays++;
793  }
794  }
795  // surface to volumes
796  meshout.surfs[i].voluind[0] = tetout.vfacetlist[i].c1 + INCR;
797  meshout.surfs[i].voluind[1] = tetout.vfacetlist[i].c2 + INCR;
798 #ifdef SAFE_ALGO
799  if (tetout.vfacetlist[i].c1 == tetout.vfacetlist[i].c2)
800  {
801  RSVS3D_ERROR_LOGIC("Tetgen outputs returns a face connected to "
802  "the same two cells");
803  }
804 #endif // SAFE_ALGO
805  n = 2;
806  for (int j = 0; j < n; ++j)
807  { // loop through corresponding connections
808  meshout.volus[meshout.surfs[i].voluind[j] - 1].surfind.push_back(i + 1);
809  }
810  }
811 
812  // Close the mesh
813  tetgen::internal::CloseVoronoiMesh(meshout, tetout, rayInd, DEINCR, boundBox);
814 
815  // Prepare mesh for use
816  for (int i = 0; i < nVolus; ++i)
817  {
818  meshout.volus[i].target = (double(rand() % 1000001) / 1000000);
819  meshout.volus[i].fill = meshout.volus[i].target;
820  meshout.volus[i].error = meshout.volus[i].target;
821  }
822  meshout.HashArray();
823  meshout.TestConnectivityBiDir(__PRETTY_FUNCTION__);
824  meshout.TightenConnectivity();
825  meshout.PrepareForUse();
826  // meshout.displight();
827 
828  auto groupedVertices = GroupCloseVertices(meshout, 1e-7);
829  auto out = meshout.MergeGroupedVertices(groupedVertices, false);
830  sort(out);
831  unique(out);
832  meshout.RemoveSingularConnectors(out);
833  meshout.HashArray();
834  meshout.PrepareForUse();
835  meshout.TightenConnectivity();
836  meshout.OrderEdges();
837  meshout.SetBorders();
838 
839 #ifdef SAFE_ALGO
840  if (meshout.TestConnectivityBiDir(__PRETTY_FUNCTION__))
841  {
842  RSVS3D_ERROR_LOGIC("Errors in connectivity detected.");
843  }
844 #endif
845 
846  FlattenBoundaryFaces(meshout);
847  meshout.PrepareForUse();
848  meshout.TightenConnectivity();
849  meshout.OrderEdges();
850  meshout.SetBorders();
851 
852 #ifdef SAFE_ALGO
853  if (meshout.TestConnectivityBiDir(__PRETTY_FUNCTION__))
854  {
855  RSVS3D_ERROR_LOGIC("Errors in connectivity detected.");
856  }
857 #endif
858 
859  return meshout;
860 }
861 
876 std::vector<int> tetgen::RSVSVoronoiMesh(const std::vector<double> &vecPts, mesh &vosMesh, mesh &snakMesh,
877  tetgen::apiparam &inparam)
878 {
879  /*
880  Generates a VOS and snaking grid from a set of points
881 
882  Args:
883  vecPts: A 1 dimensional vector containing point coordinates and
884  a point metric which will be used as the fill of the vosMesh.
885  vosMesh: a reference to an empty mesh object in which the VOS mesh
886  will be stored.
887  snakMesh: a reference to an empty mesh object in which a snaking mesh
888  will be stored.
889 
890  Steps:
891  1. Voronoi the set of points
892  2. Tetrahedralize the voronoi diagram
893  3. Use flooding to establish the relationship between voronoi cells
894  and new cells
895  4. Establish the containments of the original set of points matching
896  the additional data to the cells.
897  5. Prep meshes, as parent child
898  6. Use that info to establish containments of the original points.
899 
900  Step 3 - detail:
901  1. Mark each point in the original facets with -1.
902  2. Flood all points in the tetrahedralisation with the same number
903  not allowing a point with a -1 to be processed.
904  3. Transfer these point blocks to the tetrahedrons.
905  4. Remove all points which are marked with block numbers to generate
906  the parent mesh.
907 
908  */
909  mesh voroMesh;
910  std::vector<int> vertsVoro;
911  std::vector<int> vertBlock, elmMapping;
912 
913  // Step 1 - 2
914  std::cout << "tetrahedralisation: ";
915  auto boundFaces = tetgen::voronoi::Points2VoroAndTetmesh(vecPts, voroMesh, snakMesh, inparam);
916  std::cout << "done. ";
917 
918  std::cout << "connection(vols): ";
919  int nBlocks = snakMesh.ConnectedVolumes(elmMapping, boundFaces);
920  std::cout << "done. ";
921 
922 #ifdef SAFE_ALGO
923  if (nBlocks != voroMesh.volus.size())
924  {
925  std::cerr << "Error : nBlocks (" << nBlocks << ")!=nVolus Voromesh (" << voroMesh.volus.size() << ")"
926  << std::endl;
927  RSVS3D_ERROR_LOGIC("Voromesh and flooding do not match");
928  }
929 #endif // SAFE_ALGO
930  auto elmMappingCopy = elmMapping;
931  for (int i = 0; i < nBlocks; ++i)
932  {
933  int indRep = -1;
934  int jStart = -1;
935  do
936  {
937  jStart++;
938  if (elmMappingCopy[jStart] == i + 1)
939  {
940  indRep = snakMesh.volus(jStart)->index;
941  }
942  } while (indRep == -1);
943  for (int j = jStart; j < int(elmMapping.size()); ++j)
944  {
945  if (elmMappingCopy[j] == i + 1)
946  {
947  elmMapping[j] = indRep;
948  }
949  }
950  }
951  std::cout << "parenting: ";
952  CoarsenMesh(snakMesh, vosMesh, elmMapping);
953  snakMesh.AddParent(&vosMesh, elmMapping);
954  snakMesh.PrepareForUse();
955  snakMesh.OrientFaces();
956  vosMesh.PrepareForUse();
957  std::cout << "done. ";
958 #ifdef SAFE_ALGO
959  if (vosMesh.volus.size() != voroMesh.volus.size())
960  {
961  std::cerr << "Error : vosMesh (" << vosMesh.volus.size() << ")!=nVolus Voromesh (" << voroMesh.volus.size()
962  << ")" << std::endl;
963  RSVS3D_ERROR_LOGIC("Voromesh and vosMesh do not match");
964  }
965  if (vosMesh.TestConnectivityBiDir(__PRETTY_FUNCTION__))
966  {
967  RSVS3D_ERROR_LOGIC("Invalid VOS mesh generated.");
968  };
969 #endif // SAFE_ALGO
970  // Step 6 - Check original points containments
971  std::vector<int> vecPtsMapping = snakMesh.VertexInVolume(vecPts, 4);
972 #ifdef RSVS_DIAGNOSTIC_RESOLVED
973  DisplayVector(vecPtsMapping);
974  cout << endl;
975 #endif // RSVS_DIAGNOSTIC_RESOLVED
976  int count = vosMesh.volus.size();
977  for (int i = 0; i < count; ++i)
978  {
979  vosMesh.volus[i].fill = 0.0;
980  }
981  int nVecMap = vecPtsMapping.size();
982  for (int i = 0; i < nVecMap; ++i)
983  {
984  vosMesh
985  .volus[vosMesh.volus.find(elmMapping[snakMesh.volus.find(vecPtsMapping[i])],
986  true) // turn off warning
987  ]
988  .fill = vecPts[i * 4 + 3];
989  }
990  for (int i = 0; i < count; ++i)
991  {
992  vosMesh.volus[i].target = vosMesh.volus[i].fill;
993  vosMesh.volus[i].error = 0.0;
994  }
995  std::cout << "Finishing: ";
996  vosMesh.PrepareForUse();
997  vosMesh.OrientFaces();
998  vosMesh.SetBorders();
999  snakMesh.SetBorders();
1000  std::cout << "finished. ";
1001  std::cout << std::endl;
1002 
1003  return vecPtsMapping;
1004 }
1005 
1017 void tetgen::SnakeToSU2(const snake &snakein, const std::string &fileName, tetgen::apiparam &inparam)
1018 {
1019  tetgen::io_safe tetin, tetout;
1020 
1021  tetgen::input::RSVS2CFD(snakein, tetin, inparam);
1022 
1023  // Tetrahedralize the PLC. Switches are chosen to read a PLC (p),
1024  // do quality mesh generation (q) with a specified quality bound
1025  // (1.414), and apply a maximum volume constraint (a0.1).
1026  inparam.command = "pkqm";
1027  rsvstetrahedralize(inparam.command.c_str(), &tetin, &tetout);
1028 
1029  tetgen::output::SU2(fileName.c_str(), tetout);
1030 }
1031 
1044 {
1045  /*
1046  Translates a tetgen output to the RSVS native mesh format
1047  */
1048  mesh meshout;
1049 
1050  int nVerts, nEdges, nSurfs, nVolus;
1051  int count, INCR, DEINCR, n;
1052 
1053  nVerts = tetout.numberofpoints;
1054  nEdges = tetout.numberofedges;
1055  nSurfs = tetout.numberoftrifaces;
1056  nVolus = tetout.numberoftetrahedra;
1057 
1058  // INCR and DEINCR convert from tetgen array position to index and
1059  // index to array position respectively
1060  INCR = tetout.firstnumber ? 0 : 1;
1061  DEINCR = tetout.firstnumber ? -1 : 0;
1062 
1063  // if `tetrahedralize` has not been run with the correct flags connectivity
1064  // is unavailable, throw an error. (tests for flag -nn)
1065  if (tetout.tet2facelist == NULL)
1066  {
1067  RSVS3D_ERROR_ARGUMENT("TET2MESH requires flag -nn be passed to tetgen");
1068  }
1069 
1070  meshout.Init(nVerts, nEdges, nSurfs, nVolus);
1071  meshout.PopulateIndices();
1072 
1073  std::cout << __FUNCTION__ << " converting tetrahedralisation of size ";
1074  std::cout << std::endl;
1075  meshout.displight();
1076  std::cout << std::endl;
1077 
1078  // These need to be cleared so that push_back works as expected
1079  for (int i = 0; i < nSurfs; ++i)
1080  {
1081  meshout.surfs[i].voluind.clear();
1082  }
1083  for (int i = 0; i < nEdges; ++i)
1084  {
1085  meshout.edges[i].vertind.clear();
1086  }
1087 
1088  // Assign coordinates
1089  std::cout << "verts: ";
1090  count = nVerts;
1091  n = 3;
1092  for (int i = 0; i < count; ++i)
1093  {
1094  for (int j = 0; j < n; ++j)
1095  {
1096  meshout.verts[i].coord[j] = tetout.pointlist[i * n + j];
1097  }
1098  }
1099  std::cout << "done. ";
1100 
1101  // assign edge-vertex connectivity, relies on the implied ordering and
1102  // indexing of the output tetgenio object an of `PopulateIndices()`
1103  std::cout << "edges: ";
1104  count = nEdges;
1105  for (int i = 0; i < count; ++i)
1106  { // Assign edge to vertex connectivity
1107  meshout.edges[i].vertind = {tetout.edgelist[i * 2] + INCR, tetout.edgelist[i * 2 + 1] + INCR};
1108  for (int j = 0; j < 2; ++j)
1109  { // Assign equivalent vertex to edge connectivity
1110  meshout.verts[tetout.edgelist[i * 2 + j] + DEINCR].edgeind.push_back(i + 1);
1111  }
1112  }
1113  std::cout << "done. ";
1114 
1115  // Volumes to surfaces
1116  std::cout << "volus: ";
1117  count = nVolus;
1118  n = 4;
1119  for (int i = 0; i < count; ++i)
1120  { // loop through volus
1121  for (int j = 0; j < n; ++j)
1122  { // loop through corresponding connections
1123  meshout.volus[i].surfind.push_back(tetout.tet2facelist[i * n + j] + INCR);
1124  meshout.surfs[tetout.tet2facelist[i * n + j] + DEINCR].voluind.push_back(i + 1);
1125  }
1126  }
1127  for (int i = 0; i < nSurfs; ++i)
1128  {
1129  if (meshout.surfs[i].voluind.size() == 1)
1130  {
1131  meshout.surfs[i].voluind.push_back(0);
1132  }
1133  }
1134  std::cout << "done. ";
1135 
1136  // Surfaces to edges
1137  std::cout << "surfs: ";
1138  count = nSurfs;
1139  n = 3;
1140  for (int i = 0; i < count; ++i)
1141  { // loop through volus
1142  for (int j = 0; j < n; ++j)
1143  { // loop through corresponding connections
1144  meshout.surfs[i].edgeind.push_back(tetout.face2edgelist[i * n + j] + INCR);
1145  meshout.edges[tetout.face2edgelist[i * n + j] + DEINCR].surfind.push_back(i + 1);
1146  }
1147  }
1148  std::cout << "done. ";
1149 
1150  // Prepare mesh for use
1151  std::cout << "finishing: ";
1152  for (int i = 0; i < nVolus; ++i)
1153  {
1154  meshout.volus[i].target = (double(rand() % 1000001) / 1000000);
1155  meshout.volus[i].fill = meshout.volus[i].target;
1156  meshout.volus[i].error = meshout.volus[i].target;
1157  }
1158  meshout.TightenConnectivity();
1159  meshout.PrepareForUse();
1160 
1161  meshout.TestConnectivityBiDir(__PRETTY_FUNCTION__);
1162  // meshout.displight();
1163  std::cout << "finished conversion. ";
1164  std::cout << std::endl;
1165  return meshout;
1166 }
1167 
1176 void tetgen::voronoi::GenerateInternalPoints(const mesh &meshin, int nLevels, tetgen::io_safe &tetinPts)
1177 {
1178  auto vecPts = VolumeInternalLayers(meshin, nLevels);
1179  // Can't be done as causes tetgen to crash
1180  // auto vecPts2 = SurfaceInternalLayers(meshin, 0);
1181  // vecPts.insert(vecPts.end(),vecPts2.begin(),vecPts2.end());
1182  mesh meshgeom = Points2Mesh(vecPts, 3);
1183  mesh meshdomain;
1184  meshdomain.Init(0, 0, 0, 0);
1185  meshdomain.PrepareForUse();
1186  meshgeom.PrepareForUse();
1187 
1188  tetgen::internal::Mesh2TetgenioPoints(meshgeom, meshdomain, tetinPts);
1189 }
1190 
1191 tetgenmesh *tetgen::rsvstetrahedralize(const char *switches, tetgen::io_safe *in, tetgen::io_safe *out,
1192  tetgen::io_safe *addin, tetgen::io_safe *bgmin)
1193 {
1194  std::cout << std::endl << "__________________________________________________" << std::endl;
1195  std::cout << std::endl
1196  << " TetGen start (cmd: " << switches << ")" << std::endl
1197  << "__________________________________________________" << std::endl;
1198  std::cout << "____ in "; //<< std::endl;
1199  in->displaystats();
1200  // in->displaypoints();
1201  if (addin != NULL)
1202  {
1203  std::cout << "____ addin "; //<< std::endl;
1204  addin->displaystats();
1205  }
1206  if (bgmin != NULL)
1207  {
1208  std::cout << "____ bgmin "; //<< std::endl;
1209  bgmin->displaystats();
1210  }
1211  std::cout << "___________________________________" << std::endl;
1212 
1213  auto meshout = tetrahedralize(switches, in, out, addin, bgmin);
1214  std::cout << "___________________________________" << std::endl;
1215  std::cout << "____ out "; //<< std::endl;
1216  out->displaystats();
1217  std::cout << std::endl << "__________________________________________________" << std::endl;
1218  std::cout << std::endl
1219  << " TetGen end" << std::endl
1220  << "__________________________________________________" << std::endl;
1221  return meshout;
1222 }
1223 
1224 std::vector<bool> tetgen::voronoi::Points2VoroAndTetmesh(const std::vector<double> &vecPts, mesh &voroMesh,
1225  mesh &tetMesh, const tetgen::apiparam &inparam)
1226 {
1227  /*
1228  Turns a set of points into the voronisation and tetrahedralisation.
1229  */
1230  tetgen::io_safe tetinV, tetoutV, tetinT, tetoutT, tetinSupport;
1231  mesh ptsMesh = Points2Mesh(vecPts, 4);
1232  std::string cmd;
1233  int INCR;
1234  std::vector<int> voroPts;
1235  std::vector<bool> tetSurfInVoro;
1236  std::vector<double> lowerB, upperB;
1237 
1238  tetgen::input::POINTGRIDS(ptsMesh, tetinV, inparam, true);
1239  cmd = "Qv";
1240  rsvstetrahedralize(cmd.c_str(), &tetinV, &tetoutV);
1241  voroMesh = tetgen::output::VORO2MESH(tetoutV);
1242  // Crop mesh
1243  lowerB.assign(3, 0.0);
1244  upperB.assign(3, 0.0);
1245  for (int i = 0; i < 3; ++i)
1246  {
1247  double delta = inparam.upperB[i] - inparam.lowerB[i];
1248  lowerB[i] = inparam.lowerB[i] - delta * inparam.distanceTol * 0.999;
1249  upperB[i] = inparam.upperB[i] + delta * inparam.distanceTol * 0.999;
1250  }
1251  // voroMesh.displight();
1252  voroMesh.TestConnectivityBiDir(__PRETTY_FUNCTION__);
1253  voroMesh.CropAtBoundary(lowerB, upperB);
1254  voroMesh.TightenConnectivity();
1255  voroMesh.OrderEdges();
1256  voroMesh.SetBorders();
1257  // voroMesh.displight();
1258  TriangulateAllFaces(voroMesh);
1259  voroMesh.PrepareForUse();
1260  voroMesh.TightenConnectivity();
1261  voroMesh.OrderEdges();
1262  voroMesh.SetBorders();
1263  // Input mesh
1264 
1265  std::vector<int> vertPerSubDomain;
1266  mesh meshdomain = BuildCutCellDomain({0, 0, 0}, {0, 0, 0}, // upper bounds are not used
1267  inparam.lowerB, inparam.upperB, 1, vertPerSubDomain);
1268  tetgen::input::RSVSGRIDS(voroMesh, tetinT, inparam);
1269 
1270  int nInternalLayers = 0;
1271 
1272  if (inparam.edgelengths.size() > 1)
1273  {
1274  nInternalLayers = round(inparam.edgelengths[1]);
1275  }
1276  tetgen::voronoi::GenerateInternalPoints(voroMesh, nInternalLayers, tetinSupport);
1277 
1278  cmd = "QpqminnefO9/7";
1279  try
1280  {
1281  rsvstetrahedralize(cmd.c_str(), &tetinT, &tetoutT, &tetinSupport);
1282  }
1283  catch (exception const &ex)
1284  {
1285  std::cerr << std::endl << "Input points: ";
1286  DisplayVector(vecPts);
1287  std::cerr << std::endl;
1288 
1289  tetinT.save_poly("dbg/tetgen_error");
1290  tetinT.save_nodes("dbg/tetgen_error");
1291  voroMesh.displight();
1292  throw ex;
1293  }
1294  tetMesh = tetgen::output::TET2MESH(tetoutT);
1295 
1296  INCR = tetoutT.firstnumber ? 0 : 1;
1297 
1298  for (int i = 0; i < tetoutT.numberoftrifaces; ++i)
1299  {
1300  if (tetoutT.trifacemarkerlist[i] != 0)
1301  {
1302  for (int j = 0; j < 3; ++j)
1303  {
1304  voroPts.push_back(tetoutT.trifacelist[i * 3 + j] + INCR);
1305  }
1306  }
1307  }
1308  sort(voroPts);
1309  unique(voroPts);
1310  tetSurfInVoro.reserve(tetoutT.numberoftrifaces);
1311 
1312  for (int i = 0; i < tetoutT.numberoftrifaces; ++i)
1313  {
1314  tetSurfInVoro.push_back(tetoutT.trifacemarkerlist[i] != 0);
1315  }
1316 
1317  return (tetSurfInVoro);
1318 }
1319 
1320 std::vector<bool> tetgen::voronoi::BoundaryFacesFromPoints(const mesh &meshin, const std::vector<int> &boundaryPts)
1321 {
1322  /*
1323 
1324  */
1325 
1326  int nSurfs, nVerts;
1327  std::vector<bool> boundaryFaces, boundaryVertsLog;
1328  std::vector<int> tempVerts;
1329 
1330  nVerts = meshin.verts.size();
1331  nSurfs = meshin.surfs.size();
1332 
1333  boundaryVertsLog.assign(nVerts, false);
1334  boundaryFaces.assign(nSurfs, false);
1335  int count = boundaryPts.size();
1336  for (int i = 0; i < count; ++i)
1337  {
1338  boundaryVertsLog[meshin.verts.find(boundaryPts[i])] = true;
1339  }
1340  for (int i = 0; i < nSurfs; ++i)
1341  {
1342  tempVerts.clear();
1343  meshin.surfs(i)->OrderedVerts(&meshin, tempVerts);
1344  bool isBound = true;
1345  for (auto j : tempVerts)
1346  {
1347  isBound = isBound && boundaryVertsLog[meshin.verts.find(j)];
1348  if (!isBound)
1349  {
1350  break;
1351  }
1352  }
1353  boundaryFaces[i] = isBound;
1354  }
1355  return boundaryFaces;
1356 }
1357 
1358 void tetgen::io_safe::allocate()
1359 {
1360  // Allocation of points and associated attributes
1361  if (this->numberofpoints > 0)
1362  {
1363  this->pointlist = new REAL[this->numberofpoints * 3];
1364  this->pointattributelist = new REAL[this->numberofpoints * this->numberofpointattributes];
1365  this->pointmtrlist = new REAL[this->numberofpoints * this->numberofpointmtrs];
1366  this->pointmarkerlist = new int[this->numberofpoints];
1367  }
1368  // Allocate tetrahedron
1369  if (this->numberoftetrahedra > 0)
1370  {
1371  this->tetrahedronlist = new int[this->numberoftetrahedra * this->numberofcorners];
1372  this->tetrahedronattributelist = new REAL[this->numberoftetrahedra];
1373  this->tetrahedronvolumelist = new REAL[this->numberoftetrahedra * this->numberoftetrahedronattributes];
1374  // this->neighborlist = new int[this->numberoftetrahedra*4]; //output only
1375  }
1376  // Allocation of facets
1377  if (this->numberoffacets > 0)
1378  {
1379  this->facetlist = new tetgenio::facet[this->numberoffacets];
1380  this->facetmarkerlist = new int[this->numberoffacets];
1381  for (int i = 0; i < this->numberoffacets; ++i)
1382  {
1383  this->facetlist[i].numberofpolygons = 0;
1384  this->facetlist[i].numberofholes = 0;
1385  }
1386  }
1387  // Allocation of facet constraints
1388  if (this->numberoffacetconstraints > 0)
1389  {
1390  // [boundary marker, area constraint value]
1391  this->facetconstraintlist = new REAL[this->numberoffacetconstraints * 2];
1392  }
1393  // Allocation of holes (a set of points)
1394  if (this->numberofholes > 0)
1395  {
1396  this->holelist = new REAL[this->numberofholes * 3];
1397  }
1398  // Allocation of regions (a set of points with attributes)
1399  // X, Y, Z, region attribute at index, maximum volume at index
1400  if (this->numberofregions > 0)
1401  {
1402  this->regionlist = new REAL[this->numberofregions * 5];
1403  }
1404 
1405  // Allocate constraint
1406  if (this->numberofsegmentconstraints > 0)
1407  {
1408  this->segmentconstraintlist = new REAL[this->numberofsegmentconstraints * 3];
1409  }
1410  // Allocate triangles
1411  if (this->numberoftrifaces > 0)
1412  {
1413  this->trifacelist = new int[this->numberoftrifaces * 3];
1414  this->trifacemarkerlist = new int[this->numberoftrifaces];
1415  this->o2facelist = new int[this->numberoftrifaces * 3];
1416  this->face2tetlist = new int[this->numberoftrifaces * 2];
1417  }
1418 
1419  // Allocate edges
1420  if (this->numberofedges > 0)
1421  {
1422  this->edgelist = new int[this->numberofedges * 2];
1423  this->edgemarkerlist = new int[this->numberofedges];
1424  this->o2edgelist = new int[this->numberofedges];
1425  this->edge2tetlist = new int[this->numberofedges];
1426  this->face2edgelist = new int[this->numberofedges * 3];
1427  }
1428 
1429  // Voronoi implementation
1430  if (this->numberofvedges || this->numberofvpoints || this->numberofvcells || this->numberofvfacets)
1431  {
1432  std::cerr << "Warning : tetgen::io_safe::allocate() does not support "
1433  "Voronoi variables"
1434  << std::endl;
1435  }
1436 }
1437 
1438 void tetgen::io_safe::allocatefacet(int fIndex)
1439 {
1440  if (fIndex < this->numberoffacets)
1441  {
1442  if (this->facetlist[fIndex].numberofpolygons > 0)
1443  {
1444  this->facetlist[fIndex].polygonlist = new tetgenio::polygon[this->facetlist[fIndex].numberofpolygons];
1445  }
1446  else
1447  {
1448  this->facetlist[fIndex].polygonlist = (tetgenio::polygon *)NULL;
1449  }
1450  if (this->facetlist[fIndex].numberofholes > 0)
1451  {
1452  this->facetlist[fIndex].holelist = new REAL[this->facetlist[fIndex].numberofholes];
1453  }
1454  else
1455  {
1456  this->facetlist[fIndex].holelist = (REAL *)NULL;
1457  }
1458  }
1459  else
1460  {
1461  std::cerr << "Error: Index passed to facet allocation out "
1462  "of range"
1463  << std::endl;
1464  }
1465 }
1466 void tetgen::io_safe::allocatefacetpolygon(int fIndex, int pIndex)
1467 {
1468  if (fIndex < this->numberoffacets)
1469  {
1470  if (pIndex < this->facetlist[fIndex].numberofpolygons)
1471  {
1472  if (this->facetlist[fIndex].polygonlist[pIndex].numberofvertices > 0)
1473  {
1474  this->facetlist[fIndex].polygonlist[pIndex].vertexlist =
1475  new int[this->facetlist[fIndex].polygonlist[pIndex].numberofvertices];
1476  }
1477  else
1478  {
1479  this->facetlist[fIndex].polygonlist[pIndex].vertexlist = (int *)NULL;
1480  }
1481  }
1482  else
1483  {
1484  std::cerr << "Error: Index passed to polygon allocation out "
1485  "of range"
1486  << std::endl;
1487  }
1488  }
1489  else
1490  {
1491  std::cerr << "Error: Index passed to polygon allocation "
1492  "for facet out of range"
1493  << std::endl;
1494  }
1495 }
1496 void tetgen::io_safe::allocatefacet(int fIndex, int numPoly)
1497 {
1498  /*
1499  Allocates facets specifying the number of polygons in the facet.
1500  */
1501  if (fIndex < this->numberoffacets)
1502  {
1503  this->facetlist[fIndex].numberofpolygons = numPoly;
1504  this->allocatefacet(fIndex);
1505  }
1506  else
1507  {
1508  std::cerr << "Error: Index passed to facet allocation out "
1509  "of range"
1510  << std::endl;
1511  }
1512 }
1513 void tetgen::io_safe::allocatefacetpolygon(int fIndex, int pIndex, int numVerts)
1514 {
1515  if (fIndex < this->numberoffacets)
1516  {
1517  if (pIndex < this->facetlist[fIndex].numberofpolygons)
1518  {
1519  this->facetlist[fIndex].polygonlist[pIndex].numberofvertices = numVerts;
1520  this->allocatefacetpolygon(fIndex, pIndex);
1521  }
1522  else
1523  {
1524  std::cerr << "Error: Index passed to polygon allocation out "
1525  "of range"
1526  << std::endl;
1527  }
1528  }
1529  else
1530  {
1531  std::cerr << "Error: Index passed to polygon allocation "
1532  "for facet out of range"
1533  << std::endl;
1534  }
1535 }
1536 void tetgen::io_safe::SpecifyTetPointMetric(int startPnt, int numPnt, const std::vector<double> &mtrs)
1537 {
1538  /*
1539  assigns the mtr list from `startPnt` index
1540  */
1541 
1542  int count = int(mtrs.size()) < this->numberofpointmtrs ? mtrs.size() : this->numberofpointmtrs;
1543 
1544  if ((startPnt + numPnt) > this->numberofpoints)
1545  {
1546  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too high)");
1547  }
1548  else if (startPnt < 0)
1549  {
1550  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too low)");
1551  }
1552 
1553  for (int i = startPnt; i < startPnt + numPnt; ++i)
1554  {
1555  for (int j = 0; j < count; ++j)
1556  {
1557  this->pointmtrlist[(i * this->numberofpointmtrs) + j] = mtrs[j];
1558  }
1559  }
1560 }
1561 
1562 void tetgen::io_safe::SpecifyIndividualTetPointMetric(int startPnt, int numPnt, const std::vector<double> &mtrs)
1563 {
1564  /*
1565  assigns the mtr list from `startPnt` index
1566  */
1567 
1568  if ((startPnt + numPnt) > this->numberofpoints)
1569  {
1570  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too high)");
1571  }
1572  else if (startPnt < 0)
1573  {
1574  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too low)");
1575  }
1576  else if (numPnt * this->numberofpointmtrs != int(mtrs.size()))
1577  {
1578  RSVS3D_ERROR_ARGUMENT("Metrics need to be the same size as numPnt"
1579  "the number of point metrics");
1580  }
1581  int count = mtrs.size();
1582  for (int i = 0; i < count; ++i)
1583  {
1584  this->pointmtrlist[(startPnt * this->numberofpointmtrs) + i] = mtrs[i];
1585  }
1586 }
1587 void tetgen::io_safe::SpecifyTetFacetMetric(int startPnt, int numPnt, int marker)
1588 {
1589  /*
1590  assigns the marker from `startPnt` index
1591  */
1592 
1593  if ((startPnt + numPnt) > this->numberoffacets)
1594  {
1595  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too high)");
1596  }
1597  else if (startPnt < 0)
1598  {
1599  RSVS3D_ERROR_ARGUMENT("Metrics out of range in tetgen_io (too low)");
1600  }
1601 
1602  for (int i = startPnt; i < startPnt + numPnt; ++i)
1603  {
1604  this->facetmarkerlist[i] = marker;
1605  }
1606 }
1607 
1608 void tetgen::io_safe::displaystats()
1609 {
1610  std::cout << "tetrahedralisation stats: " << std::endl;
1611  std::cout << "points " << this->numberofpoints << ", ";
1612  std::cout << "numberofpointmtrs " << this->numberofpointmtrs << ", ";
1613  std::cout << "tetrahedra " << this->numberoftetrahedra << ", ";
1614  std::cout << "facets " << this->numberoffacets << ", ";
1615  std::cout << "facetconstraints " << this->numberoffacetconstraints << ", ";
1616  std::cout << std::endl;
1617  std::cout << "holes " << this->numberofholes << ", ";
1618  std::cout << "regions " << this->numberofregions << ", ";
1619  std::cout << "segmentconstraints " << this->numberofsegmentconstraints << ", ";
1620  std::cout << "trifaces " << this->numberoftrifaces << ", ";
1621  std::cout << std::endl;
1622  std::cout << "edges " << this->numberofedges << ", ";
1623  std::cout << "vedges " << this->numberofvedges << ", ";
1624  std::cout << "vpoints " << this->numberofvpoints << ", ";
1625  std::cout << "vcells " << this->numberofvcells << ", ";
1626  std::cout << "vfacets " << this->numberofvfacets << ", ";
1627  std::cout << std::endl;
1628 }
1629 void tetgen::io_safe::displaypoints()
1630 {
1631  for (int j = 0; j < this->numberofpointmtrs; ++j)
1632  {
1633  std::cout << "metric " << j << " :";
1634  for (int i = 0; i < this->numberofpoints * this->numberofpointmtrs; i = i + this->numberofpointmtrs)
1635  {
1636  std::cout << this->pointmtrlist[i + j] << " ";
1637  }
1638  std::cout << std::endl;
1639  }
1640  for (int i = 0; i < this->numberoffacetconstraints * 2; ++i)
1641  {
1642  std::cout << this->facetconstraintlist[i] << " ";
1643  }
1644 }
1645 
1646 //================================
1647 // Tetgen to json
1648 //================================
1649 
1650 void tetgen::to_json(json &j, const tetgen::apiparam &p)
1651 {
1652  j = json{{"lowerB", p.lowerB},
1653  {"upperB", p.upperB},
1654  {"surfedgelengths", p.surfedgelengths},
1655  {"curvatureSmoothing", p.curvatureSmoothing},
1656  {"edgelengths", p.edgelengths},
1657  {"distanceTol", p.distanceTol},
1658  {"generateMeshInside", p.generateMeshInside},
1659  {"command", p.command}};
1660 }
1661 void tetgen::from_json(const json &j, tetgen::apiparam &p)
1662 {
1663  j.at("lowerB").get_to(p.lowerB);
1664  j.at("upperB").get_to(p.upperB);
1665  p.surfedgelengths = j.at("surfedgelengths").get<std::array<double, 2>>();
1666  j.at("curvatureSmoothing").get_to(p.curvatureSmoothing);
1667  p.edgelengths = j.at("edgelengths").get<std::vector<double>>();
1668  j.at("distanceTol").get_to(p.distanceTol);
1669  j.at("generateMeshInside").get_to(p.generateMeshInside);
1670  p.command = j.at("command").get<std::string>();
1671 }
1672 
1673 void tetgen::apiparam::ReadJsonString(const std::string &jsonStr)
1674 {
1675  try
1676  {
1677  json j = json::parse(jsonStr);
1678  json jparam = *this;
1679 
1680  try
1681  {
1682  j = j.unflatten();
1683  }
1684  catch (std::exception const &ex)
1685  {
1686  // if j is already not flat catch the exception and move on
1687  // TODO check the correct exception is being thrown (one day)
1688  }
1689  rsvsjson::flatupdate(jparam, j, false, false);
1690 
1691  *this = jparam.get<tetgen::apiparam>();
1692  }
1693  catch (std::exception const &ex)
1694  {
1695  RSVS3D_ERROR_NOTHROW((std::string("Unhandled error while parsing json"
1696  " string into tetgen parameters: \n") +
1697  jsonStr)
1698  .c_str());
1699  throw ex;
1700  }
1701 }
1702 
1703 // Test code
1704 void tetgen::test::LoadData(mesh &snakeMesh, mesh &voluMesh, snake &snakein, mesh &triMesh)
1705 {
1706  /*
1707  Loads data for tetgen testing
1708  */
1709 
1710  triangulation triRSVS;
1711  int loadErrors = 0;
1712  loadErrors += snakeMesh.read("../TESTOUT/testtetgen/SnakeMesh_181205T193158_sphere2.msh");
1713  loadErrors += voluMesh.read("../TESTOUT/testtetgen/VoluMesh_181205T193158_sphere2.msh");
1714  loadErrors += snakein.read("../TESTOUT/testtetgen/Snake_181205T193158_sphere2.3snk");
1715  if (loadErrors > 0)
1716  {
1717  RSVS3D_ERROR_ARGUMENT("Error loading test data");
1718  }
1719  snakein.SetSnakeMesh(&snakeMesh);
1720 
1721  snakeMesh.PrepareForUse();
1722  // snakeMesh.displight();
1723 
1724  voluMesh.PrepareForUse();
1725  // voluMesh.displight();
1726 
1727  snakein.PrepareForUse();
1728  // snakein.displight();
1729  snakein.AssignInternalVerts();
1730 
1731  snakeMesh.AddParent(&voluMesh);
1732 
1733  triRSVS.stattri.clear();
1734  triRSVS.trivert.clear();
1735  triRSVS.PrepareForUse();
1736  TriangulateMesh(snakeMesh, triRSVS);
1737  TriangulateSnake(snakein, triRSVS);
1738  triRSVS.PrepareForUse();
1739  triRSVS.CalcTriVertPos();
1740  MaintainTriangulateSnake(triRSVS);
1741 
1742  MeshTriangulation(triMesh, snakein.snakeconn, triRSVS.dynatri, triRSVS.trivert);
1743  // triMesh.displight();
1744  triMesh.PrepareForUse();
1745  triMesh.OrderEdges();
1746  triMesh.TestConnectivityBiDir(__PRETTY_FUNCTION__);
1747 
1748  // triMesh is the triangulation in mesh format
1749  // it is not cleaned up for tiny surfaces
1750 }
1751 
1752 int tetgen::test::api()
1753 {
1754  int errCount = 0;
1755  {
1756  tetgen::apiparam p1, p2;
1757  json j1, j2;
1758 
1759  p1.distanceTol = 1.0;
1760 
1761  j1 = p1;
1762  p2 = j1;
1763  j2 = p2;
1764 
1765  if (j1 != j2)
1766  {
1767  RSVS3D_ERROR_NOTHROW(" Assignement operator not symmetric");
1768  errCount++;
1769  }
1770  else
1771  {
1772  std::cout << "Assignement passed " << std::endl;
1773  }
1774  }
1775  {
1776  tetgen::apiparam p1(
1777  R"foo(
1778  {
1779  "/distanceTol":2.0,
1780  "/edgelengths/2":2.0
1781  }
1782  )foo");
1783  tetgen::apiparam p2;
1784  json j1, j2;
1785 
1786  if (p1.distanceTol != 2.0)
1787  {
1788  RSVS3D_ERROR_NOTHROW(" JSON Constructor not working for"
1789  " distanceTol");
1790  errCount++;
1791  }
1792  else
1793  {
1794  std::cout << "Constructor passed " << std::endl;
1795  }
1796  if (p1.edgelengths.size() != 3)
1797  {
1798  RSVS3D_ERROR_NOTHROW(" JSON Constructor operator not "
1799  "working for vector beyond bounds");
1800  std::cerr << "edgelengths.size " << p1.edgelengths.size() << std::endl;
1801  errCount++;
1802  }
1803  else
1804  {
1805  std::cout << "Constructor passed " << std::endl;
1806  }
1807  }
1808 
1809  return (errCount);
1810 }
1811 
1812 int tetgen::test::CFD()
1813 {
1814  /*CFD meshing process*/
1815  tetgen::io_safe tetin, tetout;
1816 
1817  tetgen::apiparam inparam;
1818  mesh snakeMesh, voluMesh, triMesh;
1819  snake snakein;
1820 
1821  try
1822  {
1823  inparam.lowerB = {-15.0, -15.0, -15.0};
1824  inparam.upperB = {15.0, 15.0, 15.0};
1825  inparam.distanceTol = 1e-3;
1826  inparam.edgelengths = {0.06, 0.3, 1.0, 3.0};
1827  tetgen::test::LoadData(snakeMesh, voluMesh, snakein, triMesh);
1828  tetgen::input::RSVS2CFD(snakein, tetin, inparam);
1829 
1830  tetin.save_nodes("../TESTOUT/testtetgen/rsvs_3cell_2body");
1831  tetin.save_poly("../TESTOUT/testtetgen/rsvs_3cell_2body");
1832 
1833  // Tetrahedralize the PLC. Switches are chosen to read a PLC (p),
1834  // do quality mesh generation (q) with a specified quality bound
1835  // (1.414), and apply a maximum volume constraint (a0.1).
1836  inparam.command = "Qpkqm";
1837  rsvstetrahedralize(inparam.command.c_str(), &tetin, &tetout);
1838 
1839  // tetout.save_nodes("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1840  // tetout.save_elements("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1841  // tetout.save_faces("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1842  tetgen::output::SU2("../TESTOUT/testtetgen/rsvs_3cell_2body.su2", tetout);
1843  std::cout << "Finished the tettgen process " << std::endl;
1844  }
1845  catch (exception const &ex)
1846  {
1847  cerr << "Exception: " << ex.what() << endl;
1848  return 1;
1849  }
1850  return 0;
1851 }
1852 
1853 int tetgen::test::call()
1854 {
1855  /*_RSVSgrid*/
1856  tetgen::io_safe tetin, tetin2, tetin3, tetout, tetout2, tetout3;
1857  tetgen::apiparam inparam;
1858  mesh meshdomain, meshtet, meshvoro, meshtet2;
1859  snake snakein;
1860  std::array<std::array<double, 2>, 3> dimDomain;
1861  tecplotfile tecout;
1862 
1863  try
1864  {
1865  tecout.OpenFile("../TESTOUT/trianglemeshconv.plt");
1866  inparam.lowerB = {-0.0, -0.0, -0.0};
1867  inparam.upperB = {1.0, 1.0, 1.0};
1868  inparam.distanceTol = 1e-3;
1869  // inparam.edgelengths = {0.2};
1870  inparam.edgelengths = {0.25};
1871 
1872  BuildBlockGrid({3, 1, 1}, meshdomain);
1873  dimDomain[0] = {0, 3.0};
1874  // dimDomain[0] = {0,3.0};
1875  dimDomain[1] = {0, 1.0};
1876  dimDomain[2] = {0, 1.0};
1877  meshdomain.Scale(dimDomain);
1878  meshdomain.PrepareForUse();
1879  tecout.PrintMesh(meshdomain);
1880 
1881  tetgen::input::RSVSGRIDS(meshdomain, tetin, inparam);
1882 
1883  tetin.save_nodes("../TESTOUT/testtetgen/rsvs_3cell_grid");
1884  tetin.save_poly("../TESTOUT/testtetgen/rsvs_3cell_grid");
1885 
1886  // Tetrahedralize the PLC. Switches are chosen to read a PLC (p),
1887  // do quality mesh generation (q) with a specified quality bound
1888  // (1.414), and apply a maximum volume constraint (a0.1).
1889  inparam.command = "Qpkqnnvefm";
1890  rsvstetrahedralize(inparam.command.c_str(), &tetin, &tetout);
1891 
1892  // tetout.save_nodes("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1893  // tetout.save_elements("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1894  // tetout.save_faces("../TESTOUT/testtetgen/rsvsout_3cell_2body");
1895  std::cout << "Finished the tettgen process " << std::endl;
1896  meshtet = tetgen::output::TET2MESH(tetout);
1897  tecout.PrintMesh(meshtet);
1898 
1899  std::cout << " Meshed the tetrahedralization" << std::endl;
1900  tetgen::input::POINTGRIDS(meshtet, tetin3, inparam);
1901  inparam.command = "Qv";
1902  rsvstetrahedralize(inparam.command.c_str(), &tetin3, &tetout3);
1903  meshvoro = tetgen::output::VORO2MESH(tetout3);
1904  // tecout.PrintMesh(meshvoro,0,0,rsvs3d::constants::tecplot::polygon);
1905  tecout.PrintMesh(meshvoro);
1906  tecout.PrintMesh(meshvoro, 0, 0, rsvs3d::constants::tecplot::line);
1907  tecout.PrintMesh(meshvoro, 0, 0, rsvs3d::constants::tecplot::point);
1908  std::cout << " Meshed the voronization" << std::endl;
1909 
1910  inparam.edgelengths = {0.15};
1911  meshvoro.PrepareForUse();
1912 
1913  std::vector<int> subs;
1914  subs = ConcatenateVectorField(meshvoro.volus, &volu::surfind, 0, meshvoro.volus.size());
1915  std::cout << "Number of surfs " << subs.size() << std::endl;
1916  sort(subs);
1917  unique(subs);
1918  std::cout << "Number of surfs " << subs.size() << std::endl;
1919  inparam.command = "Qpkqnnefm";
1920  tetgen::input::RSVSGRIDS(meshvoro, tetin2, inparam);
1921 
1922  tetin2.save_nodes("../TESTOUT/testtetgen/rsvs_voro_grid");
1923  tetin2.save_poly("../TESTOUT/testtetgen/rsvs_voro_grid");
1924 
1925  rsvstetrahedralize(inparam.command.c_str(), &tetin2, &tetout2);
1926  std::cout << "Finished the tettgen process " << std::endl;
1927  meshtet2 = tetgen::output::TET2MESH(tetout2);
1928  tecout.PrintMesh(meshtet2);
1929  }
1930  catch (exception const &ex)
1931  {
1932  cerr << "Exception: " << ex.what() << endl;
1933  return 1;
1934  }
1935  return 0;
1936 }
1937 int tetgen::test::RSVSVORO()
1938 {
1939  std::vector<double> vecPts, vecPts2;
1940  mesh vosMesh, snakMesh;
1941  tecplotfile tecout;
1942 
1943  int nErrors = 0;
1944  const char *tecoutStr = "../TESTOUT/rsvs_voro.plt";
1945  vector<int> numCells = {0, 1, 2, 3, 4, 5, 10, 20, 100, 1000};
1946  vector<double> numEdge = {0.05, 0.1, 0.3};
1947  // std :: cin >> nPts;
1948  tecout.OpenFile(tecoutStr);
1949  int i2 = 0;
1950  for (auto i : numCells)
1951  {
1952  for (int k = i2 * 4; k < i * 4; ++k)
1953  {
1954  vecPts2.push_back(double(abs(rand()) % 32767) / 32767.0);
1955  }
1956  vecPts = vecPts2;
1957  i2 = i;
1958  for (int k = 0; k < 8; ++k)
1959  {
1960  vecPts.push_back((k % 2));
1961  vecPts.push_back(((k / 2) % 2));
1962  vecPts.push_back(((k / 4) % 2));
1963  vecPts.push_back(0);
1964  }
1965  for (auto j : numEdge)
1966  {
1967  nErrors += tetgen::test::RSVSVOROFunc(vecPts, j, tecoutStr);
1968  }
1969  vecPts.clear();
1970  }
1971 
1972  return nErrors;
1973 }
1974 
1975 int tetgen::test::RSVSVOROFunc_contain(int nPts, double distanceTol, const char *tecoutStr)
1976 {
1977  std::vector<double> vecPts;
1978  mesh vosMesh, snakMesh, meshPts;
1979  tecplotfile tecout;
1980  tetgen::apiparam inparam;
1981  try
1982  {
1983  cout << "__________________________________________________" << endl;
1984  cout << "Start Voronoi mesh generation for " << nPts << " points" << endl;
1985  cout << "__________________________________________________" << endl;
1986  for (int i = 0; i < nPts * 4; ++i)
1987  {
1988  vecPts.push_back(double(abs(rand()) % 32767) / 32767.0);
1989  }
1990  for (int i = 0; i < 8; ++i)
1991  {
1992  vecPts.push_back((i % 2));
1993  vecPts.push_back(((i / 2) % 2));
1994  vecPts.push_back(((i / 4) % 2));
1995  vecPts.push_back(0);
1996  }
1997  // tecout.OpenFile(tecoutStr, "a");
1998  tecout.OpenFile(tecoutStr, "a");
1999 
2000  inparam.lowerB = {-0.0, -0.0, -0.0};
2001  inparam.upperB = {1.0, 1.0, 1.0};
2002  inparam.distanceTol = 1e-3;
2003  // inparam.edgelengths = {0.2};
2004  inparam.edgelengths = {0.1};
2005  inparam.distanceTol = distanceTol;
2006 
2007  auto vecMap = tetgen::RSVSVoronoiMesh(vecPts, vosMesh, snakMesh, inparam);
2008 
2009  meshPts.Init(nPts + 8, 0, 0, 0);
2010  meshPts.PopulateIndices();
2011  for (int i = 0; i < nPts + 8; ++i)
2012  {
2013  meshPts.verts[i].coord.assign(vecPts.begin() + i * 4, vecPts.begin() + i * 4 + 3);
2014  }
2015  meshPts.PrepareForUse();
2016  // tecout.PrintMesh(meshPts,0,nPts+distanceTol,rsvs3d::constants::tecplot::point);
2017  int nVecMap = vecMap.size();
2018  for (int i = 0; i < nVecMap; ++i)
2019  {
2020  std::cout << snakMesh.ParentElementIndex(vecMap[i]) << " ";
2021  tecout.PrintMesh(vosMesh, nPts * 3 + 1, i, 5, {snakMesh.ParentElementIndex(vecMap[i])});
2022  tecout.PrintMesh(snakMesh, nPts * 3 + 2, i, 5, {vecMap[i]});
2023  tecout.PrintMesh(meshPts, nPts * 3 + 3, i, rsvs3d::constants::tecplot::point, {i + 1});
2024  }
2025  cout << "__________________________________________________" << endl;
2026  }
2027  catch (exception const &ex)
2028  {
2029  cerr << "Exception: " << ex.what() << endl;
2030  cerr << "for " << __PRETTY_FUNCTION__ << "with nPoints = " << nPts << endl;
2031  return 1;
2032  }
2033  return 0;
2034 }
2035 
2036 int tetgen::test::RSVSVOROFunc(const std::vector<double> &vecPts, double distanceTol, const char *tecoutStr)
2037 {
2038  // std::vector<double> vecPts;
2039  int nPts = vecPts.size() / 4 - 8;
2040  mesh vosMesh, snakMesh, meshPts;
2041  tecplotfile tecout;
2042  tetgen::apiparam inparam;
2043  try
2044  {
2045  cout << "__________________________________________________" << endl;
2046  cout << "Start Voronoi mesh generation for " << nPts << " points" << endl;
2047  cout << "__________________________________________________" << endl;
2048 
2049  tecout.OpenFile(tecoutStr, "a");
2050 
2051  inparam.lowerB = {-0.0, -0.0, -0.0};
2052  inparam.upperB = {1.0, 1.0, 1.0};
2053  inparam.edgelengths = {0.8};
2054  inparam.distanceTol = distanceTol;
2055 
2056  tetgen::RSVSVoronoiMesh(vecPts, vosMesh, snakMesh, inparam);
2057  tecout.PrintMesh(snakMesh, 1, nPts + distanceTol);
2058  tecout.PrintMesh(vosMesh, 2, nPts + distanceTol);
2059  meshPts.Init(nPts + 8, 0, 0, 0);
2060  meshPts.PopulateIndices();
2061  for (int i = 0; i < nPts + 8; ++i)
2062  {
2063  meshPts.verts[i].coord.assign(vecPts.begin() + i * 4, vecPts.begin() + i * 4 + 3);
2064  }
2065  meshPts.PrepareForUse();
2066  tecout.PrintMesh(meshPts, 3, nPts + distanceTol, rsvs3d::constants::tecplot::point);
2067  cout << "__________________________________________________" << endl;
2068  }
2069  catch (exception const &ex)
2070  {
2071  cerr << "Exception: " << ex.what() << endl;
2072  cerr << "for " << __PRETTY_FUNCTION__ << "with nPoints = " << nPts << endl;
2073  return 1;
2074  }
2075  return 0;
2076 }
2077 
2078 int tetgen::test::RSVSVORO_Contain()
2079 {
2080  std::vector<double> vecPts;
2081  mesh vosMesh, snakMesh;
2082  tecplotfile tecout;
2083 
2084  int nErrors = 0;
2085  const char *tecoutStr = "../TESTOUT/rsvs_voro_contain.plt";
2086  vector<int> numCells = {
2087  0, 1,
2088  // 2,3,4,5,10,20,100,1000
2089  };
2090  vector<double> numEdge = {
2091  // 0.05,
2092  0.1,
2093  // 0.3
2094  };
2095  // std :: cin >> nPts;
2096  tecout.OpenFile(tecoutStr);
2097 
2098  for (auto i : numCells)
2099  {
2100  for (auto j : numEdge)
2101  {
2102  nErrors += tetgen::test::RSVSVOROFunc_contain(i, j, tecoutStr);
2103  }
2104  }
2105 
2106  return nErrors;
2107 }
Provide std::vector container with hashed index mapping.
Class for an edge object in a mesh.
Definition: mesh.hpp:353
Class for mesh handling.
Definition: mesh.hpp:592
std::vector< int > VertexInVolume(const std::vector< double > testVertices, int sizeVert=3) const
Finds for each vertex, the volume object containing it.
Definition: mesh.cpp:492
void OrientFaces()
Orients either surfaces or edges depending on the dimensionality of the object.
Definition: mesh.cpp:4948
Definition: snake.hpp:83
Class for surface object in a mesh.
Definition: mesh.hpp:267
double distanceTol
Distance tolerance.
Definition: tetgenrsvs.hpp:147
std::vector< double > edgelengths
Controls the edgelengths at regular intervals.
Definition: tetgenrsvs.hpp:145
std::array< double, 2 > surfedgelengths
Controls the surface edgelengths in CFD in the order: {point of lowest curvature, point of highest cu...
Definition: tetgenrsvs.hpp:142
std::array< double, 3 > lowerB
Lower domain bound.
Definition: tetgenrsvs.hpp:137
std::array< double, 3 > upperB
Upper domain bound.
Definition: tetgenrsvs.hpp:139
Class for memory safe interface with tetgen.h.
Definition: tetgenrsvs.hpp:51
Class for a vertex in a mesh.
Definition: mesh.hpp:448
Provides all the mesh tools used for the generation of 3D grids and geometries.
mesh Points2Mesh(const std::vector< double > &vecPts, int nProp=3)
Takes in a set of points and returns a mesh of points ready for voronisation.
Definition: mesh.cpp:6293
Tools for the mathematical processing of meshes.
std::vector< double > CalculateVertexCurvature(const mesh &meshin, int smoothingSteps)
Calculates the vertex curvature.
mesh BuildCutCellDomain(const std::array< double, 3 > &outerLowerB, const std::array< double, 3 > &outerUpperB, const std::array< double, 3 > &innerLowerB, const std::array< double, 3 > &innerUpperB, int nSteps, std::vector< int > &vertPerSubDomain)
Builds a series of domains with different edge properties controlling the interpolation of the metric...
std::vector< double > VolumeInternalLayers(const mesh &meshin, int nLayers)
Returns points on edges between volume pseudo centroid and vertices.
std::vector< double > CalculateVertexMaxEdgeLength(const mesh &meshin)
Calculates the vertex maximum edge length.
void PrepareSnakeForCFD(const snake &snakein, double distanceTol, mesh &meshgeom, std::vector< double > &holeCoords)
Prepares the snake to be used for CFD, removes duplicate points and triangulates it.
Tools for the refinement and coarsening of meshes.
void CoarsenMesh(const mesh &meshchild, mesh &newparent, const std::vector< int > &elmMapping)
Provide tecplot file formating for mesh and snake outputs.
Interface between the RSVS project and the JSON for Modern C++ library.
Provides the core restricted surface snake container.
Interface between the RSVS project and tetgen.
mesh TET2MESH(tetgen::io_safe &tetout)
Translates a tetgen output to the RSVS native mesh format.
std::array< std::array< double, 3 >, 2 > dombounds
Type defining domain boundaries.
Definition: tetgenrsvs.hpp:40
void SU2(const char *fileName, const tetgenio &tetout)
Definition: tetgenrsvs.cpp:413
std::vector< int > RSVSVoronoiMesh(const std::vector< double > &vecPts, mesh &vosMesh, mesh &snakMesh, tetgen::apiparam &inparam)
Genrates Snaking and VOS RSVS meshes from the voronoi diagram of a set of points.
Definition: tetgenrsvs.cpp:876
void SnakeToSU2(const snake &snakein, const std::string &fileName, tetgen::apiparam &inparam)
Genrates an SU2 mesh file from a snake.
void GenerateInternalPoints(const mesh &meshin, int nLevels, tetgen::io_safe &tetinPts)
Generate points inside volume cells of a mesh.
Provides a triangulation for snake and meshes.
Generation of cartesian grids.
Provides the error and warning system used by the RSVS3D project.
#define RSVS3D_ERROR_LOGIC(M)
Throw a logic_error.
Definition: warning.hpp:139
#define RSVS3D_ERROR_NOTHROW(M)
Generic rsvs warning.
Definition: warning.hpp:120
#define RSVS3D_ERROR_ARGUMENT(M)
Throw a invalid_argument.
Definition: warning.hpp:148