MueLu Version of the Day
Loading...
Searching...
No Matches
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
25namespace MueLu {
26
27template <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
41template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
42void 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
51template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
52void 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
85template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
86void 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
97 TEUCHOS_TEST_FOR_EXCEPTION(aggNodes.size() == 0, Exceptions::RuntimeError,
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);
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 });
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;
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;
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--;
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
179template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
180void 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
193 TEUCHOS_TEST_FOR_EXCEPTION(aggNodes.size() == 0, Exceptions::RuntimeError,
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
510template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
511void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::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 */) {
512 ArrayView<const Scalar> values;
513
514 std::vector<std::pair<int, int> > vert1; // vertices (node indices)
515
516 ArrayView<const LocalOrdinal> indices;
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
544template <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
551template <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
556template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
560
561template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
563 return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
564}
565
566template <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
572template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
574 return sqrt(dotProduct(vec, vec));
575}
576
577template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
579 return sqrt(dotProduct(vec, vec));
580}
581
582template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
584 return mymagnitude(vecSubtract(p1, p2));
585}
586
587template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
589 return mymagnitude(vecSubtract(p1, p2));
590}
591
592template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
596
597template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
601
602template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
603myVec2 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
608template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
609myVec3 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)
615template <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 {
646 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError,
647 "VisualizationHelpers::pointDistFromTri: Could not determine the distance of a point to a triangle.");
648 }
649 }
650 return rv;
651}
652
653template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
654std::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
790template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
791std::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) {
792 TEUCHOS_TEST_FOR_EXCEPTION(points.size() < 3, Exceptions::RuntimeError,
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
882template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
883std::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
912template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
913std::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
923template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
924std::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
930template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
931std::string VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::getBaseFileName(int numProcs, int level, const Teuchos::ParameterList& pL) const {
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
947template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
948std::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
956template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
957void 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
968template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
969void 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
987template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
988void 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
1013template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1014void VisualizationHelpers<Scalar, LocalOrdinal, GlobalOrdinal, Node>::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 {
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
1034template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1035void 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
1085template <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
1092template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1093void 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
1131template <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) {
1148 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError,
1149 "VisualizationHelpers::buildColormap: Error while building colormap file: " << e.what());
1150 }
1151}
1152
1153} // namespace MueLu
1154
1155#endif /* MUELU_VISUALIZATIONHELPERS_DEF_HPP_ */
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultGlobalOrdinal GlobalOrdinal
Exception throws to report errors in the internal logical of the program.
void writeFileVTKData(std::ofstream &fout, std::vector< int > &uniqueFine, LocalOrdinal myAggOffset, ArrayRCP< LocalOrdinal > &vertex2AggId, int myRank) const
static int ccw(const myVec2 &a, const myVec2 &b, const myVec2 &c)
static double pointDistFromTri(myVec3 point, myVec3 v1, myVec3 v2, myVec3 v3)
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 writeFileVTKClosing(std::ofstream &fout) const
static myVec3 crossProduct(myVec3 v1, myVec3 v2)
std::string replaceAll(std::string result, const std::string &replaceWhat, const std::string &replaceWithWhat) const
static myVec2 vecSubtract(myVec2 v1, myVec2 v2)
std::string getBaseFileName(int numProcs, int level, const Teuchos::ParameterList &pL) const
std::string getFileName(int numProcs, int myRank, int level, const Teuchos::ParameterList &pL) const
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
std::vector< int > makeUnique(std::vector< int > &vertices) const
replaces node indices in vertices with compressed unique indices, and returns list of unique points
static double dotProduct(myVec2 v1, myVec2 v2)
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)
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 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)
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
static bool isInFront(myVec3 point, myVec3 inPlane, myVec3 n)
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 void doPointCloud(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes)
RCP< ParameterList > GetValidParameterList() const
void writePVTU(std::ofstream &pvtu, std::string baseFname, int numProcs, bool bFineEdges=false, bool bCoarseEdges=false) const
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)
static void doJacks(std::vector< int > &vertices, std::vector< int > &geomSizes, LO numLocalAggs, LO numFineNodes, const std::vector< bool > &isRoot, const ArrayRCP< LO > &vertex2AggId)
void writeFileVTKNodes(std::ofstream &fout, std::vector< int > &uniqueFine, Teuchos::RCP< const Map > &nodeMap) const
Namespace for MueLu classes and methods.
void replaceAll(std::string &str, const std::string &from, const std::string &to)
std::string toString(const T &what)
Little helper function to convert non-string types to strings.