MueLu  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MueLu_VisualizationHelpers_def.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // MueLu: A package for multigrid based preconditioning
4 //
5 // Copyright 2012 NTESS and the MueLu contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef MUELU_VISUALIZATIONHELPERS_DEF_HPP_
11 #define MUELU_VISUALIZATIONHELPERS_DEF_HPP_
12 
13 #include <Xpetra_Matrix.hpp>
14 #include <Xpetra_CrsMatrixWrap.hpp>
15 #include <Xpetra_ImportFactory.hpp>
16 #include <Xpetra_MultiVectorFactory.hpp>
18 #include "MueLu_Level.hpp"
19 
20 #include <vector>
21 #include <list>
22 #include <algorithm>
23 #include <string>
24 
25 namespace MueLu {
26 
27 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
29  RCP<ParameterList> validParamList = rcp(new ParameterList());
30 
31  validParamList->set<std::string>("visualization: output filename", "viz%LEVELID", "filename for VTK-style visualization output");
32  validParamList->set<int>("visualization: output file: time step", 0, "time step variable for output file name"); // Remove me?
33  validParamList->set<int>("visualization: output file: iter", 0, "nonlinear iteration variable for output file name"); // Remove me?
34  validParamList->set<std::string>("visualization: style", "Point Cloud", "style of aggregate visualization for VTK output. Can be 'Point Cloud', 'Jacks', 'Convex Hulls'");
35  validParamList->set<bool>("visualization: build colormap", false, "Whether to build a random color map in a separate xml file.");
36  validParamList->set<bool>("visualization: fine graph edges", false, "Whether to draw all fine node connections along with the aggregates.");
37 
38  return validParamList;
39 }
40 
41 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
42 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::doPointCloud(std::vector<int>& vertices, std::vector<int>& geomSizes, LO /* numLocalAggs */, LO numFineNodes) {
43  vertices.reserve(numFineNodes);
44  geomSizes.reserve(numFineNodes);
45  for (LocalOrdinal i = 0; i < numFineNodes; i++) {
46  vertices.push_back(i);
47  geomSizes.push_back(1);
48  }
49 }
50 
51 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
52 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::doJacks(std::vector<int>& vertices, std::vector<int>& geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector<bool>& isRoot, const ArrayRCP<LO>& vertex2AggId) {
53  // For each aggregate, find the root node then connect it with the other nodes in the aggregate
54  // Doesn't matter the order, as long as all edges are found.
55  vertices.reserve(vertices.size() + 3 * (numFineNodes - numLocalAggs));
56  geomSizes.reserve(vertices.size() + 2 * (numFineNodes - numLocalAggs));
57  int root = 0;
58  for (int i = 0; i < numLocalAggs; i++) // TODO: Replace this O(n^2) with a better way
59  {
60  while (!isRoot[root])
61  root++;
62  int numInAggFound = 0;
63  for (int j = 0; j < numFineNodes; j++) {
64  if (j == root) // don't make a connection from the root to itself
65  {
66  numInAggFound++;
67  continue;
68  }
69  if (vertex2AggId[root] == vertex2AggId[j]) {
70  vertices.push_back(root);
71  vertices.push_back(j);
72  geomSizes.push_back(2);
73  // Also draw the free endpoint explicitly for the current line
74  vertices.push_back(j);
75  geomSizes.push_back(1);
76  numInAggFound++;
77  // if(numInAggFound == aggSizes_[vertex2AggId_[root]]) //don't spend more time looking if done with that root
78  // break;
79  }
80  }
81  root++; // get set up to look for the next root
82  }
83 }
84 
85 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
86 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::doConvexHulls2D(std::vector<int>& vertices, std::vector<int>& geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector<bool>& /* isRoot */, const ArrayRCP<LO>& vertex2AggId, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& xCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& yCoords) {
87  // This algorithm is based on Andrew's Monotone Chain variant of the Graham Scan for Convex Hulls. It adds
88  // a colinearity check which we'll need for our viz.
89  for (int agg = 0; agg < numLocalAggs; agg++) {
90  std::vector<int> aggNodes;
91  for (int i = 0; i < numFineNodes; i++) {
92  if (vertex2AggId[i] == agg)
93  aggNodes.push_back(i);
94  }
95 
96  // have a list of nodes in the aggregate
98  "CoarseningVisualization::doConvexHulls2D: aggregate contains zero nodes!");
99  if (aggNodes.size() == 1) {
100  vertices.push_back(aggNodes.front());
101  geomSizes.push_back(1);
102  continue;
103  }
104  if (aggNodes.size() == 2) {
105  vertices.push_back(aggNodes.front());
106  vertices.push_back(aggNodes.back());
107  geomSizes.push_back(2);
108  continue;
109  } else {
110  int N = (int)aggNodes.size();
111  using MyPair = std::pair<myVec2, int>;
112  std::vector<MyPair> pointsAndIndex(N);
113  for (int i = 0; i < N; i++) {
114  pointsAndIndex[i] = std::make_pair(myVec2(xCoords[aggNodes[i]], yCoords[aggNodes[i]]), i);
115  }
116 
117  // Sort by x coordinate
118  std::sort(pointsAndIndex.begin(), pointsAndIndex.end(), [](const MyPair& a, const MyPair& b) {
119  return a.first.x < b.first.x || (a.first.x == b.first.x && a.first.y < b.first.y);
120  });
121 
122  // Colinearity check
123  bool colinear = true;
124  for (int i = 0; i < N; i++) {
125  if (ccw(pointsAndIndex[i].first, pointsAndIndex[(i + 1) % N].first, pointsAndIndex[(i + 2) % N].first) != 0) {
126  colinear = false;
127  break;
128  }
129  }
130 
131  if (colinear) {
132  vertices.push_back(aggNodes[pointsAndIndex.front().second]);
133  vertices.push_back(aggNodes[pointsAndIndex.back().second]);
134  geomSizes.push_back(2);
135  } else {
136  std::vector<int> hull(2 * N);
137  int count = 0;
138 
139  // Build lower hull
140  for (int i = 0; i < N; i++) {
141  while (count >= 2 && ccw(pointsAndIndex[hull[count - 2]].first, pointsAndIndex[hull[count - 1]].first, pointsAndIndex[i].first) <= 0) {
142  count--;
143  }
144  hull[count++] = i;
145  }
146 
147  // Build the upper hull
148  int t = count + 1;
149  for (int i = N - 1; i > 0; i--) {
150  while (count >= t && ccw(pointsAndIndex[hull[count - 2]].first, pointsAndIndex[hull[count - 1]].first, pointsAndIndex[i - 1].first) <= 0) {
151  count--;
152  }
153  hull[count++] = i - 1;
154  }
155 
156  // Remove the duplicated point
157  hull.resize(count - 1);
158 
159  // Verify: Check that hull retains CCW order
160  for (int i = 0; i < (int)hull.size(); i++) {
161  TEUCHOS_TEST_FOR_EXCEPTION(ccw(pointsAndIndex[hull[i]].first, pointsAndIndex[hull[(i + 1) % hull.size()]].first, pointsAndIndex[hull[(i + 1) % hull.size()]].first) == 1, Exceptions::RuntimeError, "CoarseningVisualization::doConvexHulls2D: counter-clockwise verification fails");
162  }
163 
164  // We now need to undo the indices from the sorting
165  for (int i = 0; i < (int)hull.size(); i++) {
166  hull[i] = aggNodes[pointsAndIndex[hull[i]].second];
167  }
168 
169  vertices.reserve(vertices.size() + hull.size());
170  for (size_t i = 0; i < hull.size(); i++) {
171  vertices.push_back(hull[i]);
172  }
173  geomSizes.push_back(hull.size());
174  } // else colinear
175  } // else 3 + nodes
176  }
177 }
178 
179 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
180 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::doConvexHulls3D(std::vector<int>& vertices, std::vector<int>& geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector<bool>& /* isRoot */, const ArrayRCP<LO>& vertex2AggId, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& xCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& yCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& zCoords) {
181  // Use 3D quickhull algo.
182  // Vector of node indices representing triangle vertices
183  // Note: Calculate the hulls first since will only include point data for points in the hulls
184  // Effectively the size() of vertIndices after each hull is added to it
185  typedef std::list<int>::iterator Iter;
186  for (int agg = 0; agg < numLocalAggs; agg++) {
187  std::list<int> aggNodes; // At first, list of all nodes in the aggregate. As nodes are enclosed or included by/in hull, remove them
188  for (int i = 0; i < numFineNodes; i++) {
189  if (vertex2AggId[i] == agg)
190  aggNodes.push_back(i);
191  }
192  // First, check anomalous cases
194  "CoarseningVisualization::doConvexHulls3D: aggregate contains zero nodes!");
195  if (aggNodes.size() == 1) {
196  vertices.push_back(aggNodes.front());
197  geomSizes.push_back(1);
198  continue;
199  } else if (aggNodes.size() == 2) {
200  vertices.push_back(aggNodes.front());
201  vertices.push_back(aggNodes.back());
202  geomSizes.push_back(2);
203  continue;
204  }
205  // check for collinearity
206  bool areCollinear = true;
207  {
208  Iter it = aggNodes.begin();
209  myVec3 firstVec(xCoords[*it], yCoords[*it], zCoords[*it]);
210  myVec3 comp;
211  {
212  it++;
213  myVec3 secondVec(xCoords[*it], yCoords[*it], zCoords[*it]); // cross this with other vectors to compare
214  comp = vecSubtract(secondVec, firstVec);
215  it++;
216  }
217  for (; it != aggNodes.end(); it++) {
218  myVec3 thisVec(xCoords[*it], yCoords[*it], zCoords[*it]);
219  myVec3 cross = crossProduct(vecSubtract(thisVec, firstVec), comp);
220  if (mymagnitude(cross) > 1e-10) {
221  areCollinear = false;
222  break;
223  }
224  }
225  }
226  if (areCollinear) {
227  // find the endpoints of segment describing all the points
228  // compare x, if tie compare y, if tie compare z
229  Iter min = aggNodes.begin();
230  Iter max = aggNodes.begin();
231  Iter it = ++aggNodes.begin();
232  for (; it != aggNodes.end(); it++) {
233  if (xCoords[*it] < xCoords[*min])
234  min = it;
235  else if (xCoords[*it] == xCoords[*min]) {
236  if (yCoords[*it] < yCoords[*min])
237  min = it;
238  else if (yCoords[*it] == yCoords[*min]) {
239  if (zCoords[*it] < zCoords[*min]) min = it;
240  }
241  }
242  if (xCoords[*it] > xCoords[*max])
243  max = it;
244  else if (xCoords[*it] == xCoords[*max]) {
245  if (yCoords[*it] > yCoords[*max])
246  max = it;
247  else if (yCoords[*it] == yCoords[*max]) {
248  if (zCoords[*it] > zCoords[*max])
249  max = it;
250  }
251  }
252  }
253  vertices.push_back(*min);
254  vertices.push_back(*max);
255  geomSizes.push_back(2);
256  continue;
257  }
258  bool areCoplanar = true;
259  {
260  // number of points is known to be >= 3
261  Iter vert = aggNodes.begin();
262  myVec3 v1(xCoords[*vert], yCoords[*vert], zCoords[*vert]);
263  vert++;
264  myVec3 v2(xCoords[*vert], yCoords[*vert], zCoords[*vert]);
265  vert++;
266  myVec3 v3(xCoords[*vert], yCoords[*vert], zCoords[*vert]);
267  vert++;
268  // Make sure the first three points aren't also collinear (need a non-degenerate triangle to get a normal)
269  while (mymagnitude(crossProduct(vecSubtract(v1, v2), vecSubtract(v1, v3))) < 1e-10) {
270  // Replace the third point with the next point
271  v3 = myVec3(xCoords[*vert], yCoords[*vert], zCoords[*vert]);
272  vert++;
273  }
274  for (; vert != aggNodes.end(); vert++) {
275  myVec3 pt(xCoords[*vert], yCoords[*vert], zCoords[*vert]);
276  if (fabs(pointDistFromTri(pt, v1, v2, v3)) > 1e-12) {
277  areCoplanar = false;
278  break;
279  }
280  }
281  if (areCoplanar) {
282  // do 2D convex hull
283  myVec3 planeNorm = getNorm(v1, v2, v3);
284  planeNorm.x = fabs(planeNorm.x);
285  planeNorm.y = fabs(planeNorm.y);
286  planeNorm.z = fabs(planeNorm.z);
287  std::vector<myVec2> points;
288  std::vector<int> nodes;
289  if (planeNorm.x >= planeNorm.y && planeNorm.x >= planeNorm.z) {
290  // project points to yz plane to make hull
291  for (Iter it = aggNodes.begin(); it != aggNodes.end(); it++) {
292  nodes.push_back(*it);
293  points.push_back(myVec2(yCoords[*it], zCoords[*it]));
294  }
295  }
296  if (planeNorm.y >= planeNorm.x && planeNorm.y >= planeNorm.z) {
297  // use xz
298  for (Iter it = aggNodes.begin(); it != aggNodes.end(); it++) {
299  nodes.push_back(*it);
300  points.push_back(myVec2(xCoords[*it], zCoords[*it]));
301  }
302  }
303  if (planeNorm.z >= planeNorm.x && planeNorm.z >= planeNorm.y) {
304  for (Iter it = aggNodes.begin(); it != aggNodes.end(); it++) {
305  nodes.push_back(*it);
306  points.push_back(myVec2(xCoords[*it], yCoords[*it]));
307  }
308  }
309  std::vector<int> convhull2d = giftWrap(points, nodes, xCoords, yCoords);
310  geomSizes.push_back(convhull2d.size());
311  vertices.reserve(vertices.size() + convhull2d.size());
312  for (size_t i = 0; i < convhull2d.size(); i++)
313  vertices.push_back(convhull2d[i]);
314  continue;
315  }
316  }
317  Iter exIt = aggNodes.begin(); // iterator to be used for searching for min/max x/y/z
318  int extremeSix[] = {*exIt, *exIt, *exIt, *exIt, *exIt, *exIt}; // nodes with minimumX, maxX, minY ...
319  exIt++;
320  for (; exIt != aggNodes.end(); exIt++) {
321  Iter it = exIt;
322  if (xCoords[*it] < xCoords[extremeSix[0]] ||
323  (xCoords[*it] == xCoords[extremeSix[0]] && yCoords[*it] < yCoords[extremeSix[0]]) ||
324  (xCoords[*it] == xCoords[extremeSix[0]] && yCoords[*it] == yCoords[extremeSix[0]] && zCoords[*it] < zCoords[extremeSix[0]]))
325  extremeSix[0] = *it;
326  if (xCoords[*it] > xCoords[extremeSix[1]] ||
327  (xCoords[*it] == xCoords[extremeSix[1]] && yCoords[*it] > yCoords[extremeSix[1]]) ||
328  (xCoords[*it] == xCoords[extremeSix[1]] && yCoords[*it] == yCoords[extremeSix[1]] && zCoords[*it] > zCoords[extremeSix[1]]))
329  extremeSix[1] = *it;
330  if (yCoords[*it] < yCoords[extremeSix[2]] ||
331  (yCoords[*it] == yCoords[extremeSix[2]] && zCoords[*it] < zCoords[extremeSix[2]]) ||
332  (yCoords[*it] == yCoords[extremeSix[2]] && zCoords[*it] == zCoords[extremeSix[2]] && xCoords[*it] < xCoords[extremeSix[2]]))
333  extremeSix[2] = *it;
334  if (yCoords[*it] > yCoords[extremeSix[3]] ||
335  (yCoords[*it] == yCoords[extremeSix[3]] && zCoords[*it] > zCoords[extremeSix[3]]) ||
336  (yCoords[*it] == yCoords[extremeSix[3]] && zCoords[*it] == zCoords[extremeSix[3]] && xCoords[*it] > xCoords[extremeSix[3]]))
337  extremeSix[3] = *it;
338  if (zCoords[*it] < zCoords[extremeSix[4]] ||
339  (zCoords[*it] == zCoords[extremeSix[4]] && xCoords[*it] < xCoords[extremeSix[4]]) ||
340  (zCoords[*it] == zCoords[extremeSix[4]] && xCoords[*it] == xCoords[extremeSix[4]] && yCoords[*it] < yCoords[extremeSix[4]]))
341  extremeSix[4] = *it;
342  if (zCoords[*it] > zCoords[extremeSix[5]] ||
343  (zCoords[*it] == zCoords[extremeSix[5]] && xCoords[*it] > xCoords[extremeSix[5]]) ||
344  (zCoords[*it] == zCoords[extremeSix[5]] && xCoords[*it] == xCoords[extremeSix[5]] && yCoords[*it] > zCoords[extremeSix[5]]))
345  extremeSix[5] = *it;
346  }
347  myVec3 extremeVectors[6];
348  for (int i = 0; i < 6; i++) {
349  myVec3 thisExtremeVec(xCoords[extremeSix[i]], yCoords[extremeSix[i]], zCoords[extremeSix[i]]);
350  extremeVectors[i] = thisExtremeVec;
351  }
352  double maxDist = 0;
353  int base1 = 0; // ints from 0-5: which pair out of the 6 extreme points are the most distant? (indices in extremeSix and extremeVectors)
354  int base2 = 0;
355  for (int i = 0; i < 5; i++) {
356  for (int j = i + 1; j < 6; j++) {
357  double thisDist = distance(extremeVectors[i], extremeVectors[j]);
358  // Want to make sure thisDist is significantly larger than maxDist to prevent roundoff errors from impacting node choice.
359  if (thisDist > maxDist && thisDist - maxDist > 1e-12) {
360  maxDist = thisDist;
361  base1 = i;
362  base2 = j;
363  }
364  }
365  }
366  std::list<myTriangle> hullBuilding; // each Triangle is a triplet of nodes (int IDs) that form a triangle
367  // remove base1 and base2 iters from aggNodes, they are known to be in the hull
368  aggNodes.remove(extremeSix[base1]);
369  aggNodes.remove(extremeSix[base2]);
370  // extremeSix[base1] and [base2] still have the myVec3 representation
371  myTriangle tri;
372  tri.v1 = extremeSix[base1];
373  tri.v2 = extremeSix[base2];
374  // Now find the point that is furthest away from the line between base1 and base2
375  maxDist = 0;
376  // need the vectors to do "quadruple product" formula
377  myVec3 b1 = extremeVectors[base1];
378  myVec3 b2 = extremeVectors[base2];
379  Iter thirdNode;
380  for (Iter node = aggNodes.begin(); node != aggNodes.end(); node++) {
381  myVec3 nodePos(xCoords[*node], yCoords[*node], zCoords[*node]);
382  double dist = mymagnitude(crossProduct(vecSubtract(nodePos, b1), vecSubtract(nodePos, b2))) / mymagnitude(vecSubtract(b2, b1));
383  // Want to make sure dist is significantly larger than maxDist to prevent roundoff errors from impacting node choice.
384  if (dist > maxDist && dist - maxDist > 1e-12) {
385  maxDist = dist;
386  thirdNode = node;
387  }
388  }
389  // Now know the last node in the first triangle
390  tri.v3 = *thirdNode;
391  hullBuilding.push_back(tri);
392  myVec3 b3(xCoords[*thirdNode], yCoords[*thirdNode], zCoords[*thirdNode]);
393  aggNodes.erase(thirdNode);
394  // Find the fourth node (most distant from triangle) to form tetrahedron
395  maxDist = 0;
396  int fourthVertex = -1;
397  for (Iter node = aggNodes.begin(); node != aggNodes.end(); node++) {
398  myVec3 thisNode(xCoords[*node], yCoords[*node], zCoords[*node]);
399  double nodeDist = pointDistFromTri(thisNode, b1, b2, b3);
400  // Want to make sure nodeDist is significantly larger than maxDist to prevent roundoff errors from impacting node choice.
401  if (nodeDist > maxDist && nodeDist - maxDist > 1e-12) {
402  maxDist = nodeDist;
403  fourthVertex = *node;
404  }
405  }
406  aggNodes.remove(fourthVertex);
407  myVec3 b4(xCoords[fourthVertex], yCoords[fourthVertex], zCoords[fourthVertex]);
408  // Add three new triangles to hullBuilding to form the first tetrahedron
409  // use tri to hold the triangle info temporarily before being added to list
410  tri = hullBuilding.front();
411  tri.v1 = fourthVertex;
412  hullBuilding.push_back(tri);
413  tri = hullBuilding.front();
414  tri.v2 = fourthVertex;
415  hullBuilding.push_back(tri);
416  tri = hullBuilding.front();
417  tri.v3 = fourthVertex;
418  hullBuilding.push_back(tri);
419  // now orient all four triangles so that the vertices are oriented clockwise (so getNorm_ points outward for each)
420  myVec3 barycenter((b1.x + b2.x + b3.x + b4.x) / 4.0, (b1.y + b2.y + b3.y + b4.y) / 4.0, (b1.z + b2.z + b3.z + b4.z) / 4.0);
421  for (std::list<myTriangle>::iterator tetTri = hullBuilding.begin(); tetTri != hullBuilding.end(); tetTri++) {
422  myVec3 triVert1(xCoords[tetTri->v1], yCoords[tetTri->v1], zCoords[tetTri->v1]);
423  myVec3 triVert2(xCoords[tetTri->v2], yCoords[tetTri->v2], zCoords[tetTri->v2]);
424  myVec3 triVert3(xCoords[tetTri->v3], yCoords[tetTri->v3], zCoords[tetTri->v3]);
425  myVec3 trinorm = getNorm(triVert1, triVert2, triVert3);
426  myVec3 ptInPlane = tetTri == hullBuilding.begin() ? b1 : b4; // first triangle definitely has b1 in it, other three definitely have b4
427  if (isInFront(barycenter, ptInPlane, trinorm)) {
428  // don't want the faces of the tetrahedron to face inwards (towards barycenter) so reverse orientation
429  // by swapping two vertices
430  int temp = tetTri->v1;
431  tetTri->v1 = tetTri->v2;
432  tetTri->v2 = temp;
433  }
434  }
435  // now, have starting polyhedron in hullBuilding (all faces are facing outwards according to getNorm_) and remaining nodes to process are in aggNodes
436  // recursively, for each triangle, make a list of the points that are 'in front' of the triangle. Find the point with the maximum distance from the triangle.
437  // Add three new triangles, each sharing one edge with the original triangle but now with the most distant point as a vertex. Remove the most distant point from
438  // the list of all points that need to be processed. Also from that list remove all points that are in front of the original triangle but not in front of all three
439  // new triangles, since they are now enclosed in the hull.
440  // Construct point lists for each face of the tetrahedron individually.
441  myVec3 trinorms[4]; // normals to the four tetrahedron faces, now oriented outwards
442  int index = 0;
443  for (std::list<myTriangle>::iterator tetTri = hullBuilding.begin(); tetTri != hullBuilding.end(); tetTri++) {
444  myVec3 triVert1(xCoords[tetTri->v1], yCoords[tetTri->v1], zCoords[tetTri->v1]);
445  myVec3 triVert2(xCoords[tetTri->v2], yCoords[tetTri->v2], zCoords[tetTri->v2]);
446  myVec3 triVert3(xCoords[tetTri->v3], yCoords[tetTri->v3], zCoords[tetTri->v3]);
447  trinorms[index] = getNorm(triVert1, triVert2, triVert3);
448  index++;
449  }
450  std::list<int> startPoints1;
451  std::list<int> startPoints2;
452  std::list<int> startPoints3;
453  std::list<int> startPoints4;
454  // scope this so that 'point' is not in function scope
455  {
456  Iter point = aggNodes.begin();
457  while (!aggNodes.empty()) // this removes points one at a time as they are put in startPointsN or are already done
458  {
459  myVec3 pointVec(xCoords[*point], yCoords[*point], zCoords[*point]);
460  // Note: Because of the way the tetrahedron faces are constructed above,
461  // face 1 definitely contains b1 and faces 2-4 definitely contain b4.
462  if (isInFront(pointVec, b1, trinorms[0])) {
463  startPoints1.push_back(*point);
464  point = aggNodes.erase(point);
465  } else if (isInFront(pointVec, b4, trinorms[1])) {
466  startPoints2.push_back(*point);
467  point = aggNodes.erase(point);
468  } else if (isInFront(pointVec, b4, trinorms[2])) {
469  startPoints3.push_back(*point);
470  point = aggNodes.erase(point);
471  } else if (isInFront(pointVec, b4, trinorms[3])) {
472  startPoints4.push_back(*point);
473  point = aggNodes.erase(point);
474  } else {
475  point = aggNodes.erase(point); // points here are already inside tetrahedron.
476  }
477  }
478  // Call processTriangle for each triangle in the initial tetrahedron, one at a time.
479  }
480  typedef std::list<myTriangle>::iterator TriIter;
481  TriIter firstTri = hullBuilding.begin();
482  myTriangle start1 = *firstTri;
483  firstTri++;
484  myTriangle start2 = *firstTri;
485  firstTri++;
486  myTriangle start3 = *firstTri;
487  firstTri++;
488  myTriangle start4 = *firstTri;
489  // kick off depth-first recursive filling of hullBuilding list with all triangles in the convex hull
490  if (!startPoints1.empty())
491  processTriangle(hullBuilding, start1, startPoints1, barycenter, xCoords, yCoords, zCoords);
492  if (!startPoints2.empty())
493  processTriangle(hullBuilding, start2, startPoints2, barycenter, xCoords, yCoords, zCoords);
494  if (!startPoints3.empty())
495  processTriangle(hullBuilding, start3, startPoints3, barycenter, xCoords, yCoords, zCoords);
496  if (!startPoints4.empty())
497  processTriangle(hullBuilding, start4, startPoints4, barycenter, xCoords, yCoords, zCoords);
498  // hullBuilding now has all triangles that make up this hull.
499  // Dump hullBuilding info into the list of all triangles for the scene.
500  vertices.reserve(vertices.size() + 3 * hullBuilding.size());
501  for (TriIter hullTri = hullBuilding.begin(); hullTri != hullBuilding.end(); hullTri++) {
502  vertices.push_back(hullTri->v1);
503  vertices.push_back(hullTri->v2);
504  vertices.push_back(hullTri->v3);
505  geomSizes.push_back(3);
506  }
507  }
508 }
509 
510 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
513 
514  std::vector<std::pair<int, int> > vert1; // vertices (node indices)
515 
517  for (LocalOrdinal locRow = 0; locRow < LocalOrdinal(G->GetNodeNumVertices()); locRow++) {
518  auto neighbors = G->getNeighborVertices(locRow);
519  // Add those local indices (columns) to the list of connections (which are pairs of the form (localM, localN))
520  for (int gEdge = 0; gEdge < int(neighbors.length); ++gEdge) {
521  vert1.push_back(std::pair<int, int>(locRow, neighbors(gEdge)));
522  }
523  }
524  for (size_t i = 0; i < vert1.size(); i++) {
525  if (vert1[i].first > vert1[i].second) {
526  int temp = vert1[i].first;
527  vert1[i].first = vert1[i].second;
528  vert1[i].second = temp;
529  }
530  }
531  std::sort(vert1.begin(), vert1.end());
532  std::vector<std::pair<int, int> >::iterator newEnd = unique(vert1.begin(), vert1.end()); // remove duplicate edges
533  vert1.erase(newEnd, vert1.end());
534  // std::vector<int> points1;
535  vertices.reserve(2 * vert1.size());
536  geomSizes.reserve(vert1.size());
537  for (size_t i = 0; i < vert1.size(); i++) {
538  vertices.push_back(vert1[i].first);
539  vertices.push_back(vert1[i].second);
540  geomSizes.push_back(2);
541  }
542 }
543 
544 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
546  const double ccw_zero_threshold = 1e-8;
547  double val = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
548  return (val > ccw_zero_threshold) ? 1 : ((val < -ccw_zero_threshold) ? -1 : 0);
549 }
550 
551 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
553  return myVec3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
554 }
555 
556 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
558  return v1.x * v2.x + v1.y * v2.y;
559 }
560 
561 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
563  return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
564 }
565 
566 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
568  myVec3 rel(point.x - inPlane.x, point.y - inPlane.y, point.z - inPlane.z); // position of the point relative to the plane
569  return dotProduct(rel, n) > 1e-12 ? true : false;
570 }
571 
572 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
574  return sqrt(dotProduct(vec, vec));
575 }
576 
577 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
579  return sqrt(dotProduct(vec, vec));
580 }
581 
582 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
584  return mymagnitude(vecSubtract(p1, p2));
585 }
586 
587 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
589  return mymagnitude(vecSubtract(p1, p2));
590 }
591 
592 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
594  return myVec2(v1.x - v2.x, v1.y - v2.y);
595 }
596 
597 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
599  return myVec3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
600 }
601 
602 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
603 myVec2 VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getNorm(myVec2 v) //"normal" to a 2D vector - just rotate 90 degrees to left
604 {
605  return myVec2(v.y, -v.x);
606 }
607 
608 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
609 myVec3 VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getNorm(myVec3 v1, myVec3 v2, myVec3 v3) // normal to face of triangle (will be outward rel. to polyhedron) (v1, v2, v3 are in CCW order when normal is toward viewpoint)
610 {
611  return crossProduct(vecSubtract(v2, v1), vecSubtract(v3, v1));
612 }
613 
614 // get minimum distance from 'point' to plane containing v1, v2, v3 (or the triangle with v1, v2, v3 as vertices)
615 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
617  using namespace std;
618  myVec3 norm = getNorm(v1, v2, v3);
619  // must normalize the normal vector
620  double normScl = mymagnitude(norm);
621  double rv = 0.0;
622  if (normScl > 1e-8) {
623  norm.x /= normScl;
624  norm.y /= normScl;
625  norm.z /= normScl;
626  rv = fabs(dotProduct(norm, vecSubtract(point, v1)));
627  } else {
628  // triangle is degenerated
629  myVec3 test1 = vecSubtract(v3, v1);
630  myVec3 test2 = vecSubtract(v2, v1);
631  bool useTest1 = mymagnitude(test1) > 0.0 ? true : false;
632  bool useTest2 = mymagnitude(test2) > 0.0 ? true : false;
633  if (useTest1 == true) {
634  double normScl1 = mymagnitude(test1);
635  test1.x /= normScl1;
636  test1.y /= normScl1;
637  test1.z /= normScl1;
638  rv = fabs(dotProduct(test1, vecSubtract(point, v1)));
639  } else if (useTest2 == true) {
640  double normScl2 = mymagnitude(test2);
641  test2.x /= normScl2;
642  test2.y /= normScl2;
643  test2.z /= normScl2;
644  rv = fabs(dotProduct(test2, vecSubtract(point, v1)));
645  } else {
647  "VisualizationHelpers::pointDistFromTri: Could not determine the distance of a point to a triangle.");
648  }
649  }
650  return rv;
651 }
652 
653 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
654 std::vector<myTriangle> VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::processTriangle(std::list<myTriangle>& tris, myTriangle tri, std::list<int>& pointsInFront, myVec3& barycenter, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& xCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& yCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& zCoords) {
655  //*tri is in the tris list, and is the triangle to process here. tris is a complete list of all triangles in the hull so far. pointsInFront is only a list of the nodes in front of tri. Need coords also.
656  // precondition: each triangle is already oriented so that getNorm_(v1, v2, v3) points outward (away from interior of hull)
657  // First find the point furthest from triangle.
658  using namespace std;
659  typedef std::list<int>::iterator Iter;
660  typedef std::list<myTriangle>::iterator TriIter;
661  typedef list<pair<int, int> >::iterator EdgeIter;
662  double maxDist = 0;
663  // Need vector representations of triangle's vertices
664  myVec3 v1(xCoords[tri.v1], yCoords[tri.v1], zCoords[tri.v1]);
665  myVec3 v2(xCoords[tri.v2], yCoords[tri.v2], zCoords[tri.v2]);
666  myVec3 v3(xCoords[tri.v3], yCoords[tri.v3], zCoords[tri.v3]);
667  myVec3 farPointVec; // useful to have both the point's coordinates and it's position in the list
668  Iter farPoint = pointsInFront.begin();
669  for (Iter point = pointsInFront.begin(); point != pointsInFront.end(); point++) {
670  myVec3 pointVec(xCoords[*point], yCoords[*point], zCoords[*point]);
671  double dist = pointDistFromTri(pointVec, v1, v2, v3);
672  // Want to make sure nodeDist is significantly larger than maxDist to prevent roundoff errors from impacting node choice.
673  if (dist > maxDist && dist - maxDist > 1e-12) {
674  dist = maxDist;
675  farPointVec = pointVec;
676  farPoint = point;
677  }
678  }
679  // Find all the triangles that the point is in front of (can be more than 1)
680  // At the same time, remove them from tris, as every one will be replaced later
681  vector<myTriangle> visible; // use a list of iterators so that the underlying object is still in tris
682  for (TriIter it = tris.begin(); it != tris.end();) {
683  myVec3 vec1(xCoords[it->v1], yCoords[it->v1], zCoords[it->v1]);
684  myVec3 vec2(xCoords[it->v2], yCoords[it->v2], zCoords[it->v2]);
685  myVec3 vec3(xCoords[it->v3], yCoords[it->v3], zCoords[it->v3]);
686  myVec3 norm = getNorm(vec1, vec2, vec3);
687  if (isInFront(farPointVec, vec1, norm)) {
688  visible.push_back(*it);
689  it = tris.erase(it);
690  } else
691  it++;
692  }
693  // Figure out what triangles need to be destroyed/created
694  // First create a list of edges (as std::pair<int, int>, where the two ints are the node endpoints)
695  list<pair<int, int> > horizon;
696  // For each triangle, add edges to the list iff the edge only appears once in the set of all
697  // Have members of horizon have the lower node # first, and the higher one second
698  for (vector<myTriangle>::iterator it = visible.begin(); it != visible.end(); it++) {
699  pair<int, int> e1(it->v1, it->v2);
700  pair<int, int> e2(it->v2, it->v3);
701  pair<int, int> e3(it->v1, it->v3);
702  //"sort" the pair values
703  if (e1.first > e1.second) {
704  int temp = e1.first;
705  e1.first = e1.second;
706  e1.second = temp;
707  }
708  if (e2.first > e2.second) {
709  int temp = e2.first;
710  e2.first = e2.second;
711  e2.second = temp;
712  }
713  if (e3.first > e3.second) {
714  int temp = e3.first;
715  e3.first = e3.second;
716  e3.second = temp;
717  }
718  horizon.push_back(e1);
719  horizon.push_back(e2);
720  horizon.push_back(e3);
721  }
722  // sort based on lower node first, then higher node (lexicographical ordering built in to pair)
723  horizon.sort();
724  // Remove all edges from horizon, except those that appear exactly once
725  {
726  EdgeIter it = horizon.begin();
727  while (it != horizon.end()) {
728  int occur = count(horizon.begin(), horizon.end(), *it);
729  if (occur > 1) {
730  pair<int, int> removeVal = *it;
731  while (removeVal == *it && !(it == horizon.end()))
732  it = horizon.erase(it);
733  } else
734  it++;
735  }
736  }
737  // Now make a list of new triangles being created, each of which take 2 vertices from an edge and one from farPoint
738  list<myTriangle> newTris;
739  for (EdgeIter it = horizon.begin(); it != horizon.end(); it++) {
740  myTriangle t(it->first, it->second, *farPoint);
741  newTris.push_back(t);
742  }
743  // Ensure every new triangle is oriented outwards, using the barycenter of the initial tetrahedron
744  vector<myTriangle> trisToProcess;
745  vector<list<int> > newFrontPoints;
746  for (TriIter it = newTris.begin(); it != newTris.end(); it++) {
747  myVec3 t1(xCoords[it->v1], yCoords[it->v1], zCoords[it->v1]);
748  myVec3 t2(xCoords[it->v2], yCoords[it->v2], zCoords[it->v2]);
749  myVec3 t3(xCoords[it->v3], yCoords[it->v3], zCoords[it->v3]);
750  if (isInFront(barycenter, t1, getNorm(t1, t2, t3))) {
751  // need to swap two vertices to flip orientation of triangle
752  int temp = it->v1;
753  myVec3 tempVec = t1;
754  it->v1 = it->v2;
755  t1 = t2;
756  it->v2 = temp;
757  t2 = tempVec;
758  }
759  myVec3 outwardNorm = getNorm(t1, t2, t3); // now definitely points outwards
760  // Add the triangle to tris
761  tris.push_back(*it);
762  trisToProcess.push_back(tris.back());
763  // Make a list of the points that are in front of nextToProcess, to be passed in for processing
764  list<int> newInFront;
765  for (Iter point = pointsInFront.begin(); point != pointsInFront.end();) {
766  myVec3 pointVec(xCoords[*point], yCoords[*point], zCoords[*point]);
767  if (isInFront(pointVec, t1, outwardNorm)) {
768  newInFront.push_back(*point);
769  point = pointsInFront.erase(point);
770  } else
771  point++;
772  }
773  newFrontPoints.push_back(newInFront);
774  }
775  vector<myTriangle> allRemoved; // list of all invalid iterators that were erased by calls to processmyTriangle below
776  for (int i = 0; i < int(trisToProcess.size()); i++) {
777  if (!newFrontPoints[i].empty()) {
778  // Comparing the 'triangle to process' to the one for this call prevents infinite recursion/stack overflow.
779  // TODO: Why was it doing that? Rounding error? Make more robust fix. But this does work for the time being.
780  if (find(allRemoved.begin(), allRemoved.end(), trisToProcess[i]) == allRemoved.end() && !(trisToProcess[i] == tri)) {
781  vector<myTriangle> removedList = processTriangle(tris, trisToProcess[i], newFrontPoints[i], barycenter, xCoords, yCoords, zCoords);
782  for (int j = 0; j < int(removedList.size()); j++)
783  allRemoved.push_back(removedList[j]);
784  }
785  }
786  }
787  return visible;
788 }
789 
790 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
791 std::vector<int> VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::giftWrap(std::vector<myVec2>& points, std::vector<int>& nodes, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& xCoords, const Teuchos::ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType>& yCoords) {
793  "CoarseningVisualization::giftWrap: Gift wrap algorithm input has to have at least 3 points!");
794 
795 #if 1 // TAW's version to determine "minimal" node
796  // determine minimal x and y coordinates
797  double min_x = points[0].x;
798  double min_y = points[0].y;
799  for (std::vector<int>::iterator it = nodes.begin(); it != nodes.end(); it++) {
800  int i = it - nodes.begin();
801  if (points[i].x < min_x) min_x = points[i].x;
802  if (points[i].y < min_y) min_y = points[i].y;
803  }
804  // create dummy min coordinates
805  min_x -= 1.0;
806  min_y -= 1.0;
807  myVec2 dummy_min(min_x, min_y);
808 
809  // loop over all nodes and determine nodes with minimal distance to (min_x, min_y)
810  std::vector<int> hull;
811  myVec2 min = points[0];
812  double mindist = distance(min, dummy_min);
813  std::vector<int>::iterator minNode = nodes.begin();
814  for (std::vector<int>::iterator it = nodes.begin(); it != nodes.end(); it++) {
815  int i = it - nodes.begin();
816  if (distance(points[i], dummy_min) < mindist) {
817  mindist = distance(points[i], dummy_min);
818  min = points[i];
819  minNode = it;
820  }
821  }
822  hull.push_back(*minNode);
823 #else // Brian's code
824  std::vector<int> hull;
825  std::vector<int>::iterator minNode = nodes.begin();
826  myVec2 min = points[0];
827  for (std::vector<int>::iterator it = nodes.begin(); it != nodes.end(); it++) {
828  int i = it - nodes.begin();
829  if (points[i].x < min.x || (fabs(points[i].x - min.x) < 1e-10 && points[i].y < min.y)) {
830  min = points[i];
831  minNode = it;
832  }
833  }
834  hull.push_back(*minNode);
835 #endif
836 
837  bool includeMin = false;
838  // int debug_it = 0;
839  while (1) {
840  std::vector<int>::iterator leftMost = nodes.begin();
841  if (!includeMin && leftMost == minNode) {
842  leftMost++;
843  }
844  std::vector<int>::iterator it = leftMost;
845  it++;
846  for (; it != nodes.end(); it++) {
847  if (it == minNode && !includeMin) // don't compare to min on very first sweep
848  continue;
849  if (*it == hull.back())
850  continue;
851  // see if it is in front of line containing nodes thisHull.back() and leftMost
852  // first get the left normal of leftMost - thisHull.back() (<dy, -dx>)
853  myVec2 leftMostVec = points[leftMost - nodes.begin()];
854  myVec2 lastVec(xCoords[hull.back()], yCoords[hull.back()]);
855  myVec2 testNorm = getNorm(vecSubtract(leftMostVec, lastVec));
856  // now dot testNorm with *it - leftMost. If dot is positive, leftMost becomes it. If dot is zero, take one further from thisHull.back().
857  myVec2 itVec(xCoords[*it], yCoords[*it]);
858  double dotProd = dotProduct(testNorm, vecSubtract(itVec, lastVec));
859  if (-1e-8 < dotProd && dotProd < 1e-8) {
860  // thisHull.back(), it and leftMost are collinear.
861  // Just sum the differences in x and differences in y for each and compare to get further one, don't need distance formula
862  myVec2 itDist = vecSubtract(itVec, lastVec);
863  myVec2 leftMostDist = vecSubtract(leftMostVec, lastVec);
864  if (fabs(itDist.x) + fabs(itDist.y) > fabs(leftMostDist.x) + fabs(leftMostDist.y)) {
865  leftMost = it;
866  }
867  } else if (dotProd > 0) {
868  leftMost = it;
869  }
870  }
871  // if leftMost is min, then the loop is complete.
872  if (*leftMost == *minNode)
873  break;
874  hull.push_back(*leftMost);
875  includeMin = true; // have found second point (the one after min) so now include min in the searches
876  // debug_it ++;
877  // if(debug_it > 100) exit(0); //break;
878  }
879  return hull;
880 }
881 
882 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
883 std::vector<int> VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::makeUnique(std::vector<int>& vertices) const {
884  using namespace std;
885  vector<int> uniqueNodes = vertices;
886  sort(uniqueNodes.begin(), uniqueNodes.end());
887  vector<int>::iterator newUniqueFineEnd = unique(uniqueNodes.begin(), uniqueNodes.end());
888  uniqueNodes.erase(newUniqueFineEnd, uniqueNodes.end());
889  // uniqueNodes is now a sorted list of the nodes whose info actually goes in file
890  // Now replace values in vertices with locations of the old values in uniqueFine
891  for (int i = 0; i < int(vertices.size()); i++) {
892  int lo = 0;
893  int hi = uniqueNodes.size() - 1;
894  int mid = 0;
895  int search = vertices[i];
896  while (lo <= hi) {
897  mid = lo + (hi - lo) / 2;
898  if (uniqueNodes[mid] == search)
899  break;
900  else if (uniqueNodes[mid] > search)
901  hi = mid - 1;
902  else
903  lo = mid + 1;
904  }
905  if (uniqueNodes[mid] != search)
906  throw runtime_error("Issue in makeUnique_() - a point wasn't found in list.");
907  vertices[i] = mid;
908  }
909  return uniqueNodes;
910 }
911 
912 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
913 std::string VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::replaceAll(std::string result, const std::string& replaceWhat, const std::string& replaceWithWhat) const {
914  while (1) {
915  const int pos = result.find(replaceWhat);
916  if (pos == -1)
917  break;
918  result.replace(pos, replaceWhat.size(), replaceWithWhat);
919  }
920  return result;
921 }
922 
923 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
924 std::string VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getFileName(int numProcs, int myRank, int level, const Teuchos::ParameterList& pL) const {
925  std::string filenameToWrite = getBaseFileName(numProcs, level, pL);
926  filenameToWrite = this->replaceAll(filenameToWrite, "%PROCID", toString(myRank));
927  return filenameToWrite;
928 }
929 
930 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
932  std::string filenameToWrite = pL.get<std::string>("visualization: output filename");
933  int timeStep = pL.get<int>("visualization: output file: time step");
934  int iter = pL.get<int>("visualization: output file: iter");
935 
936  if (filenameToWrite.rfind(".vtu") == std::string::npos)
937  filenameToWrite.append(".vtu");
938  if (numProcs > 1 && filenameToWrite.rfind("%PROCID") == std::string::npos) // filename can't be identical between processsors in parallel problem
939  filenameToWrite.insert(filenameToWrite.rfind(".vtu"), "-proc%PROCID");
940 
941  filenameToWrite = this->replaceAll(filenameToWrite, "%LEVELID", toString(level));
942  filenameToWrite = this->replaceAll(filenameToWrite, "%TIMESTEP", toString(timeStep));
943  filenameToWrite = this->replaceAll(filenameToWrite, "%ITER", toString(iter));
944  return filenameToWrite;
945 }
946 
947 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
948 std::string VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getPVTUFileName(int numProcs, int /* myRank */, int level, const Teuchos::ParameterList& pL) const {
949  std::string filenameToWrite = getBaseFileName(numProcs, level, pL);
950  std::string masterStem = filenameToWrite.substr(0, filenameToWrite.rfind(".vtu"));
951  masterStem = this->replaceAll(masterStem, "%PROCID", "");
952  std::string pvtuFilename = masterStem + "-master.pvtu";
953  return pvtuFilename;
954 }
955 
956 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
957 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeFileVTKOpening(std::ofstream& fout, std::vector<int>& uniqueFine, std::vector<int>& geomSizesFine) const {
958  std::string styleName = "PointCloud"; // TODO adapt this
959 
960  std::string indent = " ";
961  fout << "<!--" << styleName << " Aggregates Visualization-->" << std::endl;
962  fout << "<VTKFile type=\"UnstructuredGrid\" byte_order=\"LittleEndian\">" << std::endl;
963  fout << " <UnstructuredGrid>" << std::endl;
964  fout << " <Piece NumberOfPoints=\"" << uniqueFine.size() << "\" NumberOfCells=\"" << geomSizesFine.size() << "\">" << std::endl;
965  fout << " <PointData Scalars=\"Node Aggregate Processor\">" << std::endl;
966 }
967 
968 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
969 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeFileVTKNodes(std::ofstream& fout, std::vector<int>& uniqueFine, Teuchos::RCP<const Map>& nodeMap) const {
970  std::string indent = " ";
971  fout << " <DataArray type=\"Int32\" Name=\"Node\" format=\"ascii\">" << std::endl;
972  indent = " ";
973  bool localIsGlobal = GlobalOrdinal(nodeMap->getGlobalNumElements()) == GlobalOrdinal(nodeMap->getLocalNumElements());
974  for (size_t i = 0; i < uniqueFine.size(); i++) {
975  if (localIsGlobal) {
976  fout << uniqueFine[i] << " "; // if all nodes are on this processor, do not map from local to global
977  } else
978  fout << nodeMap->getGlobalElement(uniqueFine[i]) << " ";
979  if (i % 10 == 9)
980  fout << std::endl
981  << indent;
982  }
983  fout << std::endl;
984  fout << " </DataArray>" << std::endl;
985 }
986 
987 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
988 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeFileVTKData(std::ofstream& fout, std::vector<int>& uniqueFine, LocalOrdinal myAggOffset, ArrayRCP<LocalOrdinal>& vertex2AggId, int myRank) const {
989  std::string indent = " ";
990  fout << " <DataArray type=\"Int32\" Name=\"Aggregate\" format=\"ascii\">" << std::endl;
991  fout << indent;
992  for (int i = 0; i < int(uniqueFine.size()); i++) {
993  fout << myAggOffset + vertex2AggId[uniqueFine[i]] << " ";
994  if (i % 10 == 9)
995  fout << std::endl
996  << indent;
997  }
998  fout << std::endl;
999  fout << " </DataArray>" << std::endl;
1000  fout << " <DataArray type=\"Int32\" Name=\"Processor\" format=\"ascii\">" << std::endl;
1001  fout << indent;
1002  for (int i = 0; i < int(uniqueFine.size()); i++) {
1003  fout << myRank << " ";
1004  if (i % 20 == 19)
1005  fout << std::endl
1006  << indent;
1007  }
1008  fout << std::endl;
1009  fout << " </DataArray>" << std::endl;
1010  fout << " </PointData>" << std::endl;
1011 }
1012 
1013 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1015  std::string indent = " ";
1016  fout << " <Points>" << std::endl;
1017  fout << " <DataArray type=\"Float64\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl;
1018  fout << indent;
1019  for (int i = 0; i < int(uniqueFine.size()); i++) {
1020  fout << fx[uniqueFine[i]] << " " << fy[uniqueFine[i]] << " ";
1021  if (dim == 2)
1022  fout << "0 ";
1023  else
1024  fout << fz[uniqueFine[i]] << " ";
1025  if (i % 3 == 2)
1026  fout << std::endl
1027  << indent;
1028  }
1029  fout << std::endl;
1030  fout << " </DataArray>" << std::endl;
1031  fout << " </Points>" << std::endl;
1032 }
1033 
1034 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1035 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writeFileVTKCells(std::ofstream& fout, std::vector<int>& /* uniqueFine */, std::vector<LocalOrdinal>& vertices, std::vector<LocalOrdinal>& geomSize) const {
1036  std::string indent = " ";
1037  fout << " <Cells>" << std::endl;
1038  fout << " <DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">" << std::endl;
1039  fout << indent;
1040  for (int i = 0; i < int(vertices.size()); i++) {
1041  fout << vertices[i] << " ";
1042  if (i % 10 == 9)
1043  fout << std::endl
1044  << indent;
1045  }
1046  fout << std::endl;
1047  fout << " </DataArray>" << std::endl;
1048  fout << " <DataArray type=\"Int32\" Name=\"offsets\" format=\"ascii\">" << std::endl;
1049  fout << indent;
1050  int accum = 0;
1051  for (int i = 0; i < int(geomSize.size()); i++) {
1052  accum += geomSize[i];
1053  fout << accum << " ";
1054  if (i % 10 == 9)
1055  fout << std::endl
1056  << indent;
1057  }
1058  fout << std::endl;
1059  fout << " </DataArray>" << std::endl;
1060  fout << " <DataArray type=\"Int32\" Name=\"types\" format=\"ascii\">" << std::endl;
1061  fout << indent;
1062  for (int i = 0; i < int(geomSize.size()); i++) {
1063  switch (geomSize[i]) {
1064  case 1:
1065  fout << "1 "; // Point
1066  break;
1067  case 2:
1068  fout << "3 "; // Line
1069  break;
1070  case 3:
1071  fout << "5 "; // Triangle
1072  break;
1073  default:
1074  fout << "7 "; // Polygon
1075  }
1076  if (i % 30 == 29)
1077  fout << std::endl
1078  << indent;
1079  }
1080  fout << std::endl;
1081  fout << " </DataArray>" << std::endl;
1082  fout << " </Cells>" << std::endl;
1083 }
1084 
1085 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1087  fout << " </Piece>" << std::endl;
1088  fout << " </UnstructuredGrid>" << std::endl;
1089  fout << "</VTKFile>" << std::endl;
1090 }
1091 
1092 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1093 void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::writePVTU(std::ofstream& pvtu, std::string baseFname, int numProcs, bool bFineEdges, bool /* bCoarseEdges */) const {
1094  // If using vtk, filenameToWrite now contains final, correct ***.vtu filename (for the current proc)
1095  // So the root proc will need to use its own filenameToWrite to make a list of the filenames of all other procs to put in
1096  // pvtu file.
1097  pvtu << "<VTKFile type=\"PUnstructuredGrid\" byte_order=\"LittleEndian\">" << std::endl;
1098  pvtu << " <PUnstructuredGrid GhostLevel=\"0\">" << std::endl;
1099  pvtu << " <PPointData Scalars=\"Node Aggregate Processor\">" << std::endl;
1100  pvtu << " <PDataArray type=\"Int32\" Name=\"Node\"/>" << std::endl;
1101  pvtu << " <PDataArray type=\"Int32\" Name=\"Aggregate\"/>" << std::endl;
1102  pvtu << " <PDataArray type=\"Int32\" Name=\"Processor\"/>" << std::endl;
1103  pvtu << " </PPointData>" << std::endl;
1104  pvtu << " <PPoints>" << std::endl;
1105  pvtu << " <PDataArray type=\"Float64\" NumberOfComponents=\"3\"/>" << std::endl;
1106  pvtu << " </PPoints>" << std::endl;
1107  for (int i = 0; i < numProcs; i++) {
1108  // specify the piece for each proc (the replaceAll expression matches what the filenames will be on other procs)
1109  pvtu << " <Piece Source=\"" << replaceAll(baseFname, "%PROCID", toString(i)) << "\"/>" << std::endl;
1110  }
1111  // reference files with graph pieces, if applicable
1112  if (bFineEdges) {
1113  for (int i = 0; i < numProcs; i++) {
1114  std::string fn = replaceAll(baseFname, "%PROCID", toString(i));
1115  pvtu << " <Piece Source=\"" << fn.insert(fn.rfind(".vtu"), "-finegraph") << "\"/>" << std::endl;
1116  }
1117  }
1118  /*if(doCoarseGraphEdges_)
1119  {
1120  for(int i = 0; i < numProcs; i++)
1121  {
1122  std::string fn = replaceAll(baseFname, "%PROCID", toString(i));
1123  pvtu << " <Piece Source=\"" << fn.insert(fn.rfind(".vtu"), "-coarsegraph") << "\"/>" << std::endl;
1124  }
1125  }*/
1126  pvtu << " </PUnstructuredGrid>" << std::endl;
1127  pvtu << "</VTKFile>" << std::endl;
1128  pvtu.close();
1129 }
1130 
1131 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1133  try {
1134  std::ofstream color("random-colormap.xml");
1135  color << "<ColorMap name=\"MueLu-Random\" space=\"RGB\">" << std::endl;
1136  // Give -1, -2, -3 distinctive colors (so that the style functions can have constrasted geometry types)
1137  // Do red, orange, yellow to constrast with cool color spectrum for other types
1138  color << " <Point x=\"" << -1 << "\" o=\"1\" r=\"1\" g=\"0\" b=\"0\"/>" << std::endl;
1139  color << " <Point x=\"" << -2 << "\" o=\"1\" r=\"1\" g=\"0.6\" b=\"0\"/>" << std::endl;
1140  color << " <Point x=\"" << -3 << "\" o=\"1\" r=\"1\" g=\"1\" b=\"0\"/>" << std::endl;
1141  srand(time(NULL));
1142  for (int i = 0; i < 5000; i += 4) {
1143  color << " <Point x=\"" << i << "\" o=\"1\" r=\"" << (rand() % 50) / 256.0 << "\" g=\"" << (rand() % 256) / 256.0 << "\" b=\"" << (rand() % 256) / 256.0 << "\"/>" << std::endl;
1144  }
1145  color << "</ColorMap>" << std::endl;
1146  color.close();
1147  } catch (std::exception& e) {
1149  "VisualizationHelpers::buildColormap: Error while building colormap file: " << e.what());
1150  }
1151 }
1152 
1153 } // namespace MueLu
1154 
1155 #endif /* MUELU_VISUALIZATIONHELPERS_DEF_HPP_ */
static void doPointCloud(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes)
MueLu::DefaultLocalOrdinal LocalOrdinal
std::string toString(const T &what)
Little helper function to convert non-string types to strings.
std::string getPVTUFileName(int numProcs, int myRank, int level, const Teuchos::ParameterList &pL) const
void writeFileVTKOpening(std::ofstream &fout, std::vector< int > &uniqueFine, std::vector< int > &geomSizesFine) const
void writeFileVTKCoordinates(std::ofstream &fout, std::vector< int > &uniqueFine, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fx, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fy, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fz, int dim) const
std::string replaceAll(std::string result, const std::string &replaceWhat, const std::string &replaceWithWhat) const
static void doConvexHulls2D(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector< bool > &isRoot, const ArrayRCP< LO > &vertex2AggId, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &xCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &yCoords)
T & get(const std::string &name, T def_value)
void writeFileVTKData(std::ofstream &fout, std::vector< int > &uniqueFine, LocalOrdinal myAggOffset, ArrayRCP< LocalOrdinal > &vertex2AggId, int myRank) const
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
std::vector< int > makeUnique(std::vector< int > &vertices) const
replaces node indices in vertices with compressed unique indices, and returns list of unique points ...
std::string getFileName(int numProcs, int myRank, int level, const Teuchos::ParameterList &pL) const
LocalOrdinal LO
ParameterList & set(std::string const &name, T &&value, std::string const &docString="", RCP< const ParameterEntryValidator > const &validator=null)
KOKKOS_INLINE_FUNCTION size_type GetNodeNumVertices() const
Return number of graph vertices.
void writeFileVTKNodes(std::ofstream &fout, std::vector< int > &uniqueFine, Teuchos::RCP< const Map > &nodeMap) const
RCP< ParameterList > GetValidParameterList() const
static int ccw(const myVec2 &a, const myVec2 &b, const myVec2 &c)
static double pointDistFromTri(myVec3 point, myVec3 v1, myVec3 v2, myVec3 v3)
void sort(View &view, const size_t &size)
static double dotProduct(myVec2 v1, myVec2 v2)
void writePVTU(std::ofstream &pvtu, std::string baseFname, int numProcs, bool bFineEdges=false, bool bCoarseEdges=false) const
std::string getBaseFileName(int numProcs, int level, const Teuchos::ParameterList &pL) const
static void doConvexHulls3D(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector< bool > &isRoot, const ArrayRCP< LO > &vertex2AggId, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &xCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &yCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &zCoords)
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
MueLu::DefaultGlobalOrdinal GlobalOrdinal
static void doJacks(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector< bool > &isRoot, const ArrayRCP< LO > &vertex2AggId)
KOKKOS_INLINE_FUNCTION neighbor_vertices_type getNeighborVertices(LO i) const
Return the list of vertices adjacent to the vertex &#39;v&#39;.
static myVec3 crossProduct(myVec3 v1, myVec3 v2)
TransListIter iter
static myVec2 vecSubtract(myVec2 v1, myVec2 v2)
static double distance(myVec2 p1, myVec2 p2)
void writeFileVTKCells(std::ofstream &fout, std::vector< int > &uniqueFine, std::vector< LocalOrdinal > &vertices, std::vector< LocalOrdinal > &geomSize) const
static bool isInFront(myVec3 point, myVec3 inPlane, myVec3 n)
Exception throws to report errors in the internal logical of the program.
static std::vector< myTriangle > processTriangle(std::list< myTriangle > &tris, myTriangle tri, std::list< int > &pointsInFront, myVec3 &barycenter, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &xCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &yCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &zCoords)
static void doGraphEdges(std::vector< int > &vertices, std::vector< int > &geomSizes, Teuchos::RCP< LWGraph > &G, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fx, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fy, Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &fz)
void replaceAll(std::string &str, const std::string &from, const std::string &to)
static std::vector< int > giftWrap(std::vector< myVec2 > &points, std::vector< int > &nodes, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &xCoords, const Teuchos::ArrayRCP< const typename Teuchos::ScalarTraits< Scalar >::coordinateType > &yCoords)
void writeFileVTKClosing(std::ofstream &fout) const