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