Nektar++
OutputVtk.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2//
3// File: OutputVtk.cpp
4//
5// For more information, please see: http://www.nektar.info/
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: VTK file format output using VTK library.
32//
33////////////////////////////////////////////////////////////////////////////////
34
35#include <boost/core/ignore_unused.hpp>
36#include <boost/format.hpp>
37
38#include "OutputVtk.h"
41
42#include <vtkCellType.h>
43#include <vtkDoubleArray.h>
44#include <vtkInformation.h>
45#include <vtkMultiBlockDataSet.h>
46#include <vtkPointData.h>
47#include <vtkPoints.h>
48#include <vtkXMLMultiBlockDataWriter.h>
49#include <vtkXMLUnstructuredGridWriter.h>
50
51namespace Nektar
52{
53namespace FieldUtils
54{
55
57 ModuleKey(eOutputModule, "vtu"), OutputVtk::create, "Writes a VTU file.");
58
60{
62 m_config["legacy"] =
63 ConfigOption(true, "0", "Output using legacy manual file writers");
64 m_config["highorder"] = ConfigOption(
65 true, "0", "Output using new high-order Lagrange elements");
66 m_config["multiblock"] = ConfigOption(
67 true, "0", "Output using multi-blocks to separate composites");
68 m_config["uncompress"] = ConfigOption(true, "0", "Uncompress xml sections");
69}
70
71// Anonymous namespace for spectral order -> VTK order mapping functions
72// be aware that these orderings are very similar to gmsh but not the same!
73namespace
74{
75// Hashing function for the ordering map taking two integers
76inline size_t key2(int i, int j)
77{
78 return (size_t)i << 32 | (unsigned int)j;
79}
80
81// Hashing function for the ordering map taking three integers
82inline size_t key3(int i, int j, int k)
83{
84 return (size_t)i << 10 ^ j << 5 ^ k;
85}
86
87// Map that takes (a,b,c) -> m
88typedef std::tuple<int, int, int> Mode;
89
90struct cmpop
91{
92 bool operator()(Mode const &a, Mode const &b) const
93 {
94 if (std::get<0>(a) < std::get<0>(b))
95 {
96 return true;
97 }
98 if (std::get<0>(a) > std::get<0>(b))
99 {
100 return false;
101 }
102 if (std::get<1>(a) < std::get<1>(b))
103 {
104 return true;
105 }
106 if (std::get<1>(a) > std::get<1>(b))
107 {
108 return false;
109 }
110 if (std::get<2>(a) < std::get<2>(b))
111 {
112 return true;
113 }
114
115 return false;
116 }
117};
118
119void Rotate(int nrot, std::vector<long long> &surfVerts)
120{
121 int n, i, j, cnt;
122 int np = static_cast<int>(
123 (sqrt(8.0 * static_cast<int>(surfVerts.size()) + 1.0) - 1) / 2);
124 std::vector<long long> tmp(np * np);
125
126 for (n = 0; n < nrot; ++n)
127 {
128 for (cnt = i = 0; i < np; ++i)
129 {
130 for (j = 0; j < np - i; ++j, cnt++)
131 {
132 tmp[i * np + j] = surfVerts[cnt];
133 }
134 }
135 for (cnt = i = 0; i < np; ++i)
136 {
137 for (j = 0; j < np - i; ++j, cnt++)
138 {
139 surfVerts[cnt] = tmp[(np - 1 - i - j) * np + i];
140 }
141 }
142 }
143}
144
145void Reflect(std::vector<long long> &surfVerts)
146{
147 int i, j, cnt;
148 int np = static_cast<int>(
149 (sqrt(8.0 * static_cast<double>(surfVerts.size()) + 1.0) - 1) / 2);
150 std::vector<long long> tmp(np * np);
151
152 for (cnt = i = 0; i < np; ++i)
153 {
154 for (j = 0; j < np - i; ++j, cnt++)
155 {
156 tmp[i * np + np - i - 1 - j] = surfVerts[cnt];
157 }
158 }
159
160 for (cnt = i = 0; i < np; ++i)
161 {
162 for (j = 0; j < np - i; ++j, cnt++)
163 {
164 surfVerts[cnt] = tmp[i * np + j];
165 }
166 }
167}
168
169void Align(std::vector<long long> thisVertId, std::vector<long long> vertId,
170 std::vector<long long> &surfVerts)
171{
172 if (vertId[0] == thisVertId[0])
173 {
174 if (vertId[1] == thisVertId[1] || vertId[1] == thisVertId[2])
175 {
176 if (vertId[1] == thisVertId[2])
177 {
178 Rotate(1, surfVerts);
179 Reflect(surfVerts);
180 }
181 }
182 }
183 else if (vertId[0] == thisVertId[1])
184 {
185 if (vertId[1] == thisVertId[0] || vertId[1] == thisVertId[2])
186 {
187 if (vertId[1] == thisVertId[0])
188 {
189 Reflect(surfVerts);
190 }
191 else
192 {
193 Rotate(2, surfVerts);
194 }
195 }
196 }
197 else if (vertId[0] == thisVertId[2])
198 {
199 if (vertId[1] == thisVertId[0] || vertId[1] == thisVertId[1])
200 {
201 if (vertId[1] == thisVertId[1])
202 {
203 Rotate(2, surfVerts);
204 Reflect(surfVerts);
205 }
206 else
207 {
208 Rotate(1, surfVerts);
209 }
210 }
211 }
212}
213
214std::vector<long long> quadTensorNodeOrdering(
215 const std::vector<long long> &nodes)
216{
217 int nN = static_cast<int>(nodes.size());
218 int n = static_cast<int>(sqrt(nN));
219
220 std::vector<long long> nodeList(nN);
221
222 // Vertices
223 nodeList[0] = nodes[0];
224 if (n > 1)
225 {
226 nodeList[n - 1] = nodes[1];
227 nodeList[n * n - 1] = nodes[2];
228 nodeList[n * (n - 1)] = nodes[3];
229 }
230
231 if (n > 2)
232 {
233 // Edge 0 -> 1
234 for (int i = 1; i < n - 1; ++i)
235 {
236 nodeList[i] = nodes[4 + i - 1];
237 }
238
239 // Edge 3 -> 2
240 for (int i = 1; i < n - 1; ++i)
241 {
242 nodeList[n * (n - 1) + i] = nodes[4 + 2 * (n - 2) + i - 1];
243 }
244
245 for (int j = 1; j < n - 1; ++j)
246 {
247 // Edge 0 -> 3
248 nodeList[n * (n - j - 1)] = nodes[4 + 3 * (n - 2) + n - 2 - j];
249
250 // Interior
251 for (int i = 1; i < n - 1; ++i)
252 {
253 nodeList[j * n + i] = nodes[(i - 3) - 2 * j + (3 + j) * n];
254 }
255
256 // Edge 1 -> 2
257 nodeList[(j + 1) * n - 1] = nodes[4 + (n - 2) + j - 1];
258 }
259 }
260
261 return nodeList;
262}
263
264std::vector<long long> triTensorNodeOrdering(
265 const std::vector<long long> &nodes)
266{
267 int nN = static_cast<int>(nodes.size());
268 int n = static_cast<int>(std::sqrt(2 * nN));
269
270 std::vector<long long> nodeList(nN);
271 int cnt2;
272
273 // Vertices
274 nodeList[0] = nodes[0];
275 if (n > 1)
276 {
277 nodeList[n - 1] = nodes[1];
278 nodeList[n * (n + 1) / 2 - 1] = nodes[2];
279 }
280
281 // Edges
282 int cnt = n;
283 for (int i = 1; i < n - 1; ++i)
284 {
285 nodeList[i] = nodes[3 + i - 1];
286 nodeList[cnt] = nodes[3 + 3 * (n - 2) - i];
287 nodeList[cnt + n - i - 1] = nodes[3 + (n - 2) + i - 1];
288 cnt += n - i;
289 }
290
291 // Interior (recursion)
292 if (n > 3)
293 {
294 // Reorder interior nodes
295 std::vector<long long> interior((n - 3) * (n - 2) / 2);
296 std::copy(nodes.begin() + 3 + 3 * (n - 2), nodes.end(),
297 interior.begin());
298 interior = triTensorNodeOrdering(interior);
299
300 // Copy into full node list
301 cnt = n;
302 cnt2 = 0;
303 for (int j = 1; j < n - 2; ++j)
304 {
305 for (int i = 0; i < n - j - 2; ++i)
306 {
307 nodeList[cnt + i + 1] = interior[cnt2 + i];
308 }
309 cnt += n - j;
310 cnt2 += n - 2 - j;
311 }
312 }
313
314 return nodeList;
315}
316
317std::vector<long long> tetTensorNodeOrdering(
318 const std::vector<long long> &nodes)
319{
320 int nN = static_cast<int>(nodes.size());
321 int n = static_cast<int>(
322 std::cbrt(3 * nN + std::sqrt(9 * nN * nN - 1 / 27)) +
323 std::cbrt(3 * nN - std::sqrt(9 * nN * nN - 1 / 27)) - 0.5);
324
325 std::vector<long long> nodeList(nN);
326 int nTri = n * (n + 1) / 2;
327 int nTet = n * (n + 1) * (n + 2) / 6;
328
329 // Vertices
330 nodeList[0] = nodes[0];
331 if (n == 1)
332 {
333 return nodeList;
334 }
335
336 nodeList[n - 1] = nodes[1];
337 nodeList[nTri - 1] = nodes[2];
338 nodeList[nTet - 1] = nodes[3];
339
340 if (n == 2)
341 {
342 return nodeList;
343 }
344
345 // Set up a map that takes (a,b,c) -> m to help us figure out where things
346 // are inside the tetrahedron.
347 std::map<Mode, int, cmpop> tmp;
348 for (int k = 0, cnt = 0; k < n; ++k)
349 {
350 for (int j = 0; j < n - k; ++j)
351 {
352 for (int i = 0; i < n - k - j; ++i)
353 {
354 tmp[Mode(i, j, k)] = cnt++;
355 }
356 }
357 }
358
359 // Edges first 3
360 for (int i = 1; i < n - 1; ++i)
361 {
362 int eI = i - 1;
363 nodeList[tmp[Mode(i, 0, 0)]] = nodes[4 + eI];
364 nodeList[tmp[Mode(n - 1 - i, i, 0)]] = nodes[4 + (n - 2) + eI];
365 nodeList[tmp[Mode(0, n - 1 - i, 0)]] = nodes[4 + 2 * (n - 2) + eI];
366 }
367
368 // Edges last 3 reversed (compared with NekMesh)
369 for (int i = 1; i < n - 1; ++i)
370 {
371 int eI = (n - 1 - i) - 1;
372 nodeList[tmp[Mode(0, 0, n - 1 - i)]] = nodes[4 + 3 * (n - 2) + eI];
373 nodeList[tmp[Mode(i, 0, n - 1 - i)]] = nodes[4 + 4 * (n - 2) + eI];
374 nodeList[tmp[Mode(0, i, n - 1 - i)]] = nodes[4 + 5 * (n - 2) + eI];
375 }
376
377 if (n == 3)
378 {
379 return nodeList;
380 }
381
382 // For faces, we use the triTensorNodeOrdering routine to make our lives
383 // slightly easier.
384 int nFacePts = (n - 3) * (n - 2) / 2;
385
386 // Grab face points and reorder into a tensor-product type format
387 std::vector<std::vector<long long>> tmpNodes(4);
388 int offset = 4 + 6 * (n - 2);
389
390 for (int i = 0; i < 4; ++i)
391 {
392 tmpNodes[i].resize(nFacePts);
393 for (int j = 0; j < nFacePts; ++j)
394 {
395 tmpNodes[i][j] = nodes[offset++];
396 }
397 tmpNodes[i] = triTensorNodeOrdering(tmpNodes[i]);
398 }
399
400 if (n > 4)
401 {
402 // Now align faces (different to NekMesh)
403 std::vector<long long> triVertId(3), toAlign(3);
404 triVertId[0] = 0;
405 triVertId[1] = 1;
406 triVertId[2] = 2;
407
408 toAlign[0] = 0;
409 toAlign[1] = 2;
410 toAlign[2] = 1;
411 Align(triVertId, toAlign, tmpNodes[2]);
412 Align(triVertId, toAlign, tmpNodes[3]);
413
414 toAlign[0] = 2;
415 toAlign[1] = 0;
416 toAlign[2] = 1;
417 Align(triVertId, toAlign, tmpNodes[1]);
418 }
419
420 // Reordered from NekMesh to put base last
421 for (int j = 1, cnt = 0; j < n - 2; ++j)
422 {
423 for (int i = 1; i < n - j - 1; ++i, ++cnt)
424 {
425 nodeList[tmp[Mode(i, j, 0)]] = tmpNodes[3][cnt];
426 nodeList[tmp[Mode(i, 0, j)]] = tmpNodes[0][cnt];
427 nodeList[tmp[Mode(n - 1 - i - j, i, j)]] = tmpNodes[1][cnt];
428 nodeList[tmp[Mode(0, i, j)]] = tmpNodes[2][cnt];
429 }
430 }
431
432 if (n == 4)
433 {
434 return nodeList;
435 }
436
437 // Finally, recurse on interior volume
438 std::vector<long long> intNodes, tmpInt;
439 for (int i = offset; i < nTet; ++i)
440 {
441 intNodes.emplace_back(nodes[i]);
442 }
443
444 tmpInt = tetTensorNodeOrdering(intNodes);
445
446 for (int k = 1, cnt = 0; k < n - 2; ++k)
447 {
448 for (int j = 1; j < n - k - 1; ++j)
449 {
450 for (int i = 1; i < n - k - j - 1; ++i)
451 {
452 nodeList[tmp[Mode(i, j, k)]] = tmpInt[cnt++];
453 }
454 }
455 }
456
457 return nodeList;
458}
459
460// This prism mapping is NOT using the standard spectral mapping! The point
461// output from ProcessEquiSpacedOutput is not the same as the rest of the code.
462std::vector<long long> prismTensorNodeOrdering(
463 const std::vector<long long> &nodes)
464{
465 int nN = static_cast<int>(nodes.size());
466 int n = static_cast<int>(std::cbrt(2 * nN));
467
468 std::vector<long long> nodeList(nN);
469 int nQuad = n * n;
470 int nPrism = n * n * (n + 1) / 2;
471 int edge = n - 2;
472
473 // Vertices
474 nodeList[0] = nodes[0];
475 if (n > 1)
476 {
477 nodeList[nPrism - n] = nodes[1];
478 nodeList[n - 1] = nodes[2];
479 nodeList[nQuad - n] = nodes[3];
480 nodeList[nPrism - 1] = nodes[4];
481 nodeList[nQuad - 1] = nodes[5];
482 }
483
484 if (n == 2)
485 {
486 return nodeList;
487 }
488
489 int nPts = 6;
490 int cnt = 0;
491 for (int i = 0; i < n - 2; ++i)
492 {
493 // Triangle 1 edges
494 cnt += n * (n - i); // This is adding the quads as they reduce a side by
495 // one each time
496 nodeList[cnt] = nodes[nPts + i]; // Edge 1
497 nodeList[cnt + edge - i] = nodes[nPts + 2 * edge - i - 1]; // Edge 2
498 nodeList[i + 1] = nodes[nPts + 3 * edge - i - 1]; // Edge 3
499 }
500
501 cnt = nQuad;
502 for (int i = 1; i < n - 1; ++i)
503 {
504 // Triangle 2 edges
505 cnt += n * (n - i); // This is adding the quads as they reduce a side by
506 // one each time
507 nodeList[cnt - n + i] = nodes[nPts + 3 * edge + i - 1]; // Edge 1 (4)
508 nodeList[cnt - 1] = nodes[nPts + 5 * edge - i]; // Edge 2 (5)
509 nodeList[nQuad - i - 1] = nodes[nPts + 5 * edge + i - 1]; // Edge 3 (6)
510 }
511
512 // Vertical edges
513 for (int i = 1; i < n - 1; ++i)
514 {
515 // Vertical prism edges
516 nodeList[n * i] = nodes[nPts + 6 * edge + i - 1]; // Edge 1 (7)
517 nodeList[nPrism - n + i] = nodes[nPts + 7 * edge + i - 1]; // Edge 2 (8)
518 nodeList[n * i + n - 1] = nodes[nPts + 8 * edge + i - 1]; // Edge 3 (9)
519 }
520
521 // Do quad faces first as when n == 3 we have no tri faces
522 int nSmallTri = (n - 3) * (n - 2) / 2;
523 int nSmallQuad = (n - 2) * (n - 2);
524 nPts = 6 + 9 * edge + 2 * nSmallTri;
525 cnt = nQuad;
526 for (int i = 1; i < n - 1; ++i)
527 {
528 for (int j = 0; j < n - 2; ++j)
529 {
530 nodeList[cnt + (n - i) + (n - i) * j] =
531 nodes[nPts + j * edge + (i - 1)];
532
533 nodeList[cnt + 2 * (n - i) - 1 + (n - i) * j] =
534 nodes[nPts + nSmallQuad + (j + 1) * edge - i];
535
536 nodeList[i * n + j + 1] =
537 nodes[nPts + 2 * nSmallQuad + i * edge - j - 1];
538 }
539
540 cnt += n * (n - i);
541 }
542
543 if (n == 3)
544 {
545 return nodeList;
546 }
547
548 // Triangular alignment
549 std::vector<long long> tmpNodes(nSmallTri);
550 std::iota(tmpNodes.begin(), tmpNodes.end(), 0);
551 if (n > 4)
552 {
553 std::vector<long long> triVertId(3), toAlign(3);
554 triVertId[0] = 0;
555 triVertId[1] = 1;
556 triVertId[2] = 2;
557
558 toAlign[0] = 0;
559 toAlign[1] = 2;
560 toAlign[2] = 1;
561 Align(triVertId, toAlign, tmpNodes);
562 }
563
564 // Triangle face 1
565 nPts = 6 + 9 * edge;
566 cnt = nQuad;
567 int cnt2 = 0;
568 for (int i = 1; i < n - 2; ++i)
569 {
570 for (int j = 1; j < n - i - 1; ++j)
571 {
572 nodeList[cnt + j] = nPts + tmpNodes[cnt2++];
573 }
574
575 cnt += n * (n - i);
576 }
577
578 // Triangle face 2
579 cnt = nQuad;
580 cnt2 = 0;
581 for (int i = 1; i < n - 2; ++i)
582 {
583 cnt += n * (n - i);
584 for (int j = 1; j < n - i - 1; ++j)
585 {
586 nodeList[cnt - (n - i) + j] = nPts + nSmallTri + tmpNodes[cnt2++];
587 }
588 }
589
590 // Triangular volume (uses same alignment mapping)
591 nPts = 6 + 9 * edge + 2 * nSmallTri + 3 * nSmallQuad;
592 for (int k = 1; k < n - 1; ++k)
593 {
594 cnt = nQuad;
595 cnt2 = 0;
596 for (int i = 1; i < n - 2; ++i)
597 {
598 for (int j = 1; j < n - i - 1; ++j)
599 {
600 nodeList[cnt + k * (n - i) + j] = nPts + tmpNodes[cnt2++];
601 }
602
603 cnt += n * (n - i);
604 }
605
606 nPts += nSmallTri;
607 }
608
609 return nodeList;
610}
611
612std::vector<long long> hexTensorNodeOrdering(
613 const std::vector<long long> &nodes)
614{
615 int nN = static_cast<int>(nodes.size());
616 int n = static_cast<int>(std::cbrt(nN));
617
618 std::vector<long long> nodeList(nN);
619
620 // Vertices
621 nodeList[0] = nodes[0];
622 if (n == 1)
623 {
624 return nodeList;
625 }
626
627 // Vertices: same order as Nektar++
628 nodeList[n - 1] = nodes[1];
629 nodeList[n * n - 1] = nodes[2];
630 nodeList[n * (n - 1)] = nodes[3];
631 nodeList[n * n * (n - 1)] = nodes[4];
632 nodeList[n - 1 + n * n * (n - 1)] = nodes[5];
633 nodeList[n * n - 1 + n * n * (n - 1)] = nodes[6];
634 nodeList[n * (n - 1) + n * n * (n - 1)] = nodes[7];
635
636 if (n == 2)
637 {
638 return nodeList;
639 }
640
641 int hexEdges[12][2] = {{0, 1},
642 {n - 1, n},
643 {n * n - 1, -1},
644 {n * (n - 1), -n},
645 {0, n * n},
646 {n - 1, n * n},
647 {n * n - 1, n * n},
648 {n * (n - 1), n * n},
649 {n * n * (n - 1), 1},
650 {n * n * (n - 1) + n - 1, n},
651 {n * n * n - 1, -1},
652 {n * n * (n - 1) + n * (n - 1), -n}};
653 int hexFaces[6][3] = {{0, 1, n}, {0, 1, n * n},
654 {n - 1, n, n * n}, {n * (n - 1), 1, n * n},
655 {0, n, n * n}, {n * n * (n - 1), 1, n}};
656
657 int gmshToNekEdge[12] = {0, 1, -2, -3, 8, 9, -10, -11, 4, 5, 6, 7};
658
659 // Edges
660 int offset = 8;
661 for (int i : gmshToNekEdge)
662 {
663 int e = abs(i);
664
665 if (i >= 0)
666 {
667 for (int j = 1; j < n - 1; ++j)
668 {
669 nodeList[hexEdges[e][0] + j * hexEdges[e][1]] = nodes[offset++];
670 }
671 }
672 else
673 {
674 for (int j = 1; j < n - 1; ++j)
675 {
676 nodeList[hexEdges[e][0] + (n - j - 1) * hexEdges[e][1]] =
677 nodes[offset++];
678 }
679 }
680 }
681
682 // Faces
683 int gmsh2NekFace[6] = {4, 2, 1, 3, 0, 5};
684
685 // Map which defines orientation between Gmsh and Nektar++ faces.
686 StdRegions::Orientation faceOrient[6] = {
693
694 for (int i = 0; i < 6; ++i)
695 {
696 int n2 = (n - 2) * (n - 2);
697 int face = gmsh2NekFace[i];
698 offset = 8 + 12 * (n - 2) + i * n2;
699
700 // Create a list of interior face nodes for this face only.
701 std::vector<long long> faceNodes(n2);
702 for (int j = 0; j < n2; ++j)
703 {
704 faceNodes[j] = nodes[offset + j];
705 }
706
707 // Now get the reordering of this face, which puts Gmsh
708 // recursive ordering into Nektar++ row-by-row order.
709 std::vector<long long> tmp(n2);
710
711 // Finally reorient the face according to the geometry
712 // differences.
713 if (faceOrient[i] == StdRegions::eDir1FwdDir1_Dir2FwdDir2)
714 {
715 // Orientation is the same, just copy.
716 tmp = faceNodes;
717 }
718 else if (faceOrient[i] == StdRegions::eDir1FwdDir2_Dir2FwdDir1)
719 {
720 // Tranposed faces
721 for (int j = 0; j < n - 2; ++j)
722 {
723 for (int k = 0; k < n - 2; ++k)
724 {
725 tmp[j * (n - 2) + k] = faceNodes[k * (n - 2) + j];
726 }
727 }
728 }
729 else if (faceOrient[i] == StdRegions::eDir1BwdDir1_Dir2FwdDir2)
730 {
731 for (int j = 0; j < n - 2; ++j)
732 {
733 for (int k = 0; k < n - 2; ++k)
734 {
735 tmp[j * (n - 2) + k] = faceNodes[j * (n - 2) + (n - k - 3)];
736 }
737 }
738 }
739
740 // Now put this into the right place in the output array
741 for (int k = 1; k < n - 1; ++k)
742 {
743 for (int j = 1; j < n - 1; ++j)
744 {
745 nodeList[hexFaces[face][0] + j * hexFaces[face][1] +
746 k * hexFaces[face][2]] =
747 faceNodes[(k - 1) * (n - 2) + j - 1];
748 }
749 }
750 }
751
752 // Finally, recurse on interior volume
753 std::vector<long long> intNodes, tmpInt;
754 for (int i = 8 + 12 * (n - 2) + 6 * (n - 2) * (n - 2); i < n * n * n; ++i)
755 {
756 intNodes.push_back(nodes[i]);
757 }
758
759 if (!intNodes.empty())
760 {
761 tmpInt = hexTensorNodeOrdering(intNodes);
762 for (int k = 1, cnt = 0; k < n - 1; ++k)
763 {
764 for (int j = 1; j < n - 1; ++j)
765 {
766 for (int i = 1; i < n - 1; ++i)
767 {
768 nodeList[i + j * n + k * n * n] = tmpInt[cnt++];
769 }
770 }
771 }
772 }
773
774 return nodeList;
775}
776
777std::vector<long long> lowOrderMapping(int nDim, Array<OneD, int> nquad)
778{
779 std::vector<long long> p;
780 switch (nDim)
781 {
782 case 3:
783 for (int j = 0; j < nquad[0] - 1; ++j)
784 {
785 for (int k = 0; k < nquad[1] - 1; ++k)
786 {
787 for (int l = 0; l < nquad[2] - 1; ++l)
788 {
789 p.insert(
790 p.end(),
791 {l * nquad[0] * nquad[1] + k * nquad[0] + j,
792 l * nquad[0] * nquad[1] + k * nquad[0] + j + 1,
793 l * nquad[0] * nquad[1] + (k + 1) * nquad[0] + j +
794 1,
795 l * nquad[0] * nquad[1] + (k + 1) * nquad[0] + j,
796 (l + 1) * nquad[0] * nquad[1] + k * nquad[0] + j,
797 (l + 1) * nquad[0] * nquad[1] + k * nquad[0] + j +
798 1,
799 (l + 1) * nquad[0] * nquad[1] +
800 (k + 1) * nquad[0] + j + 1,
801 (l + 1) * nquad[0] * nquad[1] +
802 (k + 1) * nquad[0] + j});
803 }
804 }
805 }
806 break;
807 case 2:
808 for (int j = 0; j < nquad[0] - 1; ++j)
809 {
810 for (int k = 0; k < nquad[1] - 1; ++k)
811 {
812 p.insert(p.end(), {k * nquad[0] + j, k * nquad[0] + j + 1,
813 (k + 1) * nquad[0] + j + 1,
814 (k + 1) * nquad[0] + j});
815 }
816 }
817 break;
818 case 1:
819 for (int j = 0; j < nquad[0] - 1; ++j)
820 {
821 p.insert(p.end(), {j, j + 1});
822 }
823 break;
824 default:
825 break;
826 }
827
828 return p;
829}
830
831int GetHighOrderVtkCellType(int sType)
832{
833#if VTK_MAJOR_VERSION >= 8
834 // For deformed elements this is a map of shape type to VTK type
835 static const std::map<int, int> vtkCellType = {
836 {{LibUtilities::eSegment, VTK_LAGRANGE_CURVE},
837 {LibUtilities::eTriangle, VTK_LAGRANGE_TRIANGLE},
838 {LibUtilities::eQuadrilateral, VTK_LAGRANGE_QUADRILATERAL},
839 {LibUtilities::eTetrahedron, VTK_LAGRANGE_TETRAHEDRON},
840 {LibUtilities::ePyramid, VTK_LAGRANGE_PYRAMID},
841 {LibUtilities::ePrism, VTK_LAGRANGE_WEDGE},
842 {LibUtilities::eHexahedron, VTK_LAGRANGE_HEXAHEDRON}}};
843
844 return vtkCellType.at(sType);
845#else
846 boost::ignore_unused(sType);
847 NEKERROR(
849 "High-order VTK output requires minimum VTK library version of 8.0")
850 return 0;
851#endif
852}
853
854} // namespace
855
856vtkSmartPointer<vtkUnstructuredGrid> OutputVtk::OutputFromExpLowOrder()
857{
858 // Mesh file to output
859 vtkSmartPointer<vtkUnstructuredGrid> vtkMesh =
860 vtkSmartPointer<vtkUnstructuredGrid>::New();
861
862 // Cache ordering so we aren't recreating mappings
863 std::unordered_map<size_t, std::vector<long long>> mappingCache;
864
865 // Choose cell types and num quad points based on dimension
866 int nDim = m_f->m_exp[0]->GetShapeDimension();
867 int nHomoDir = m_f->m_numHomogeneousDir;
868
869 auto type = (nDim + nHomoDir == 3) ? VTK_HEXAHEDRON
870 : (nDim + nHomoDir == 2) ? VTK_QUAD
871 : VTK_LINE;
872 int nQpts = (nDim + nHomoDir == 3) ? 8 : (nDim + nHomoDir == 2) ? 4 : 2;
873
874 // Fill homogeneous info
875 if (nHomoDir != 0)
876 {
877 ASSERTL0(nHomoDir == 1 &&
878 m_f->m_exp[0]->GetExpType() == MultiRegions::e3DH1D,
879 "Only regular expansions and the 3DH1D homogeneous expansion "
880 "are supported in the new VTK writer. Please use the 'legacy' "
881 "option for all other expansion types.")
882
883 m_numPlanes = m_f->m_exp[0]->GetHomogeneousBasis()->GetNumModes();
884 // Extra plane if fourier
885 m_extraPlane = (m_f->m_exp[0]->GetHomogeneousBasis()->GetBasisType() ==
887 m_f->m_exp[0]->GetHomogeneousBasis()->GetPointsType() ==
889 }
890
891 // Insert points
892 vtkNew<vtkPoints> vtkPoints;
893 vtkPoints->SetDataType(VTK_DOUBLE);
894
895 int offset = 0;
896 int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
897 for (int i = 0; i < nElmts; ++i)
898 {
899 Array<OneD, int> nquad(3, 0);
900 for (int j = 0; j < nDim; ++j)
901 {
902 nquad[j] = m_f->m_exp[0]->GetExp(i)->GetNumPoints(j);
903 }
904
905 // Add homogeneous direction num points
906 if (nHomoDir != 0)
907 {
908 nquad[nDim] = m_numPlanes;
909
910 // Add extra plane if fourier
911 if (m_extraPlane)
912 {
913 nquad[nDim]++;
914 }
915 }
916
917 // Get total points by multiplying all nquads together (accounts for
918 // homo)
919 int nPts = nquad[0];
920 for (int j = 1; j < nDim + nHomoDir; ++j)
921 {
922 nPts *= nquad[j];
923 }
924
925 // Get all coordinates at quadrature points in this element
926 Array<OneD, NekDouble> x(nPts, 0.0), y(nPts, 0.0), z(nPts, 0.0);
927 m_f->m_exp[0]->GetCoords(i, x, y, z);
928
929 // If add extra plane for fourier need to fill last plane coordinates
930 if (m_extraPlane)
931 {
932 // Copy x & y to extra plane
934 Vmath::Vcopy(nquad[0] * nquad[1], x, 1,
935 tmp = x + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
936 Vmath::Vcopy(nquad[0] * nquad[1], y, 1,
937 tmp = y + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
938
939 // Fill z on extra plane
940 NekDouble zDouble = z[nquad[0] * nquad[1] * (nquad[2] - 1) - 1] +
941 (z[nquad[0] * nquad[1]] - z[0]);
942 Vmath::Fill(nquad[0] * nquad[1], zDouble,
943 tmp = z + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
944 }
945
946 // Insert points in to vtk mesh
947 for (int j = 0; j < nPts; ++j)
948 {
949 vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
950 }
951
952 // Insert elements
953 auto oIt = mappingCache.find(key3(nquad[0], nquad[1], nquad[2]));
954 if (oIt == mappingCache.end())
955 {
956 auto p = lowOrderMapping(nDim + nHomoDir, nquad);
957 oIt = mappingCache
958 .insert(
959 std::make_pair(key3(nquad[0], nquad[1], nquad[2]), p))
960 .first;
961 }
962
963 auto p = oIt->second;
964 std::for_each(p.begin(), p.end(),
965 [offset](long long &d) { d += offset; });
966 for (int j = 0; j < p.size(); j += nQpts)
967 {
968 vtkMesh->InsertNextCell(type, nQpts, &p[j]);
969 }
970
971 offset += nPts;
972 }
973
974 vtkMesh->SetPoints(vtkPoints.GetPointer());
975
976 return vtkMesh;
977}
978
980 po::variables_map &vm, std::string &filename,
981 vtkSmartPointer<vtkUnstructuredGrid> &vtkMesh)
982{
983 // Insert field information we iterate by element from first plane to last,
984 // while the fld file iterates by plane from first element to last
985 int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
986 int numPtsPerPlane = m_f->m_exp[0]->GetTotPoints() / m_numPlanes;
987 for (int v = 0; v < m_f->m_variables.size(); ++v)
988 {
989 vtkNew<vtkDoubleArray> fieldData;
990 for (int i = 0; i < nElmts; ++i)
991 {
992 int elmtOffset = m_f->m_exp[v]->GetPhys_Offset(i);
993 int nPtsPerElmtPerPlane = m_f->m_exp[v]->GetExp(i)->GetTotPoints();
994
995 for (int j = 0; j < m_numPlanes; ++j)
996 {
997 int planeOffset = j * numPtsPerPlane;
998 for (int k = 0; k < nPtsPerElmtPerPlane; ++k)
999 {
1000 fieldData->InsertNextValue(
1001 m_f->m_exp[v]->GetPhys()[elmtOffset + planeOffset + k]);
1002 }
1003 }
1004
1005 // if extra plane we copy the first plane values in to last plane
1006 if (m_extraPlane)
1007 {
1008 for (int k = 0; k < nPtsPerElmtPerPlane; ++k)
1009 {
1010 fieldData->InsertNextValue(
1011 m_f->m_exp[v]->GetPhys()[elmtOffset + k]);
1012 }
1013 }
1014 }
1015
1016 fieldData->SetName(&m_f->m_variables[v][0]);
1017 vtkMesh->GetPointData()->AddArray(fieldData.GetPointer());
1018 }
1019
1020 WriteVTK(vtkMesh, filename, vm);
1021}
1022
1024 std::string &filename)
1025{
1026 ASSERTL0(
1027 m_f->m_numHomogeneousDir == 0,
1028 "Multi block VTK is not implemented for homogeneous expansion types.")
1029
1030 ASSERTL0(m_f->m_comm->GetSpaceComm()->IsSerial(),
1031 "Multi block VTK is not implemented in parallel.")
1032
1033 int dim = m_f->m_graph->GetMeshDimension();
1034
1035 // Create mappings from geometry id to expansion ids
1036 std::array<std::map<int, std::pair<int, int>>, 4> geomIdToExpId;
1037 int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
1038 for (int i = 0; i < nElmts; ++i)
1039 {
1040 auto geom = m_f->m_exp[0]->GetExp(i)->GetGeom();
1041 geomIdToExpId[geom->GetShapeDim()][geom->GetGlobalID()] =
1042 std::make_pair(i, -1);
1043
1044 for (int j = 0; j < geom->GetNumFaces(); ++j)
1045 {
1046 geomIdToExpId[2][geom->GetFid(j)] = std::make_pair(i, j);
1047 }
1048
1049 for (int j = 0; j < geom->GetNumEdges(); ++j)
1050 {
1051 geomIdToExpId[1][geom->GetEid(j)] = std::make_pair(i, j);
1052 }
1053 }
1054
1055 // Cache ordering so we aren't recreating mappings
1056 std::unordered_map<size_t, std::vector<long long>> mappingCache;
1057
1058 std::map<int, vtkNew<vtkUnstructuredGrid>> vtkMesh;
1059 std::map<int, SpatialDomains::CompositeSharedPtr> composites =
1060 m_f->m_graph->GetComposites();
1061 std::map<int, std::string> compositeNames;
1062 for (auto &comp : composites)
1063 {
1064 // Vector of field data
1065 std::vector<vtkNew<vtkDoubleArray>> fieldData(m_f->m_variables.size());
1066
1067 // Insert points
1068 vtkNew<vtkPoints> vtkPoints;
1069 vtkPoints->SetDataType(VTK_DOUBLE);
1070
1071 int compId = comp.first;
1072 std::vector<std::shared_ptr<SpatialDomains::Geometry>> geomVec =
1073 comp.second->m_geomVec;
1074
1075 unsigned int offset = 0;
1076 for (auto &geom : geomVec)
1077 {
1078 int geomId = geom->GetGlobalID();
1079 int sDim = geom->GetShapeDim();
1080 auto type = (sDim == 3) ? VTK_HEXAHEDRON
1081 : (sDim == 2) ? VTK_QUAD
1082 : VTK_LINE;
1083 int nQpts = (sDim == 3) ? 8 : (sDim == 2) ? 4 : 2;
1084
1086 m_f->m_exp[0]->GetExp(geomIdToExpId[sDim][geomId].first);
1087
1088 unsigned int nPts = exp->GetTotPoints();
1089 Array<OneD, NekDouble> x(nPts, 0.0), y(nPts, 0.0), z(nPts, 0.0);
1090 exp->GetCoords(x, y, z);
1091
1092 int offsetPhys = m_f->m_exp[0]->GetPhys_Offset(
1093 geomIdToExpId[sDim][geomId].first);
1094 if (sDim == dim)
1095 {
1096 for (int j = 0; j < nPts; ++j)
1097 {
1098 vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
1099
1100 // Add field data
1101 for (int k = 0; k < m_f->m_variables.size(); ++k)
1102 {
1103 fieldData[k]->InsertNextValue(
1104 m_f->m_exp[k]->GetPhys()[j + offsetPhys]);
1105 }
1106 }
1107 }
1108 else
1109 {
1110 Array<OneD, int> pointsMap;
1111 exp->GetTracePhysMap(geomIdToExpId[sDim][geomId].second,
1112 pointsMap);
1113 for (int j : pointsMap)
1114 {
1115 vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
1116
1117 // Add field data
1118 for (int k = 0; k < m_f->m_variables.size(); ++k)
1119 {
1120 fieldData[k]->InsertNextValue(
1121 m_f->m_exp[k]->GetPhys()[offsetPhys + j]);
1122 }
1123 }
1124
1125 exp = exp->GetTraceExp(geomIdToExpId[sDim][geomId].second);
1126 nPts = pointsMap.size();
1127 }
1128
1129 Array<OneD, int> nquad(3, 0);
1130 for (int j = 0; j < sDim; ++j)
1131 {
1132 nquad[j] = exp->GetNumPoints(j);
1133 }
1134
1135 auto oIt = mappingCache.find(key3(nquad[0], nquad[1], nquad[2]));
1136 if (oIt == mappingCache.end())
1137 {
1138 auto p = lowOrderMapping(sDim, nquad);
1139 oIt = mappingCache
1140 .insert(std::make_pair(
1141 key3(nquad[0], nquad[1], nquad[2]), p))
1142 .first;
1143 }
1144
1145 auto p = oIt->second;
1146 std::for_each(p.begin(), p.end(),
1147 [offset](long long &d) { d += offset; });
1148
1149 for (int j = 0; j < p.size(); j += nQpts)
1150 {
1151 vtkMesh[compId]->InsertNextCell(type, nQpts, &p[j]);
1152 }
1153
1154 offset += nPts;
1155 }
1156
1157 vtkMesh[compId]->SetPoints(vtkPoints.GetPointer());
1158
1159 // Add all fields to vtkMesh
1160 for (int k = 0; k < m_f->m_variables.size(); ++k)
1161 {
1162 fieldData[k]->SetName(&m_f->m_variables[k][0]);
1163 vtkMesh[compId]->GetPointData()->AddArray(
1164 fieldData[k].GetPointer());
1165 }
1166
1167 // Find composite names if exists and store in map
1168 // Set name as boundary label if it exists otherwise index ID
1169 compositeNames[compId] = "Composite ID " + std::to_string(compId);
1170 auto clabels = m_f->m_graph->GetCompositesLabels();
1171 auto oIt = clabels.find(compId);
1172 if (oIt != clabels.end())
1173 {
1174 compositeNames[compId] = oIt->second;
1175 }
1176 }
1177
1178 // Main multi-block set
1179 vtkNew<vtkMultiBlockDataSet> mainBlock;
1180
1181 std::set<int> compSet;
1182
1183 // Fill domain multi blocks from composites
1184 vtkNew<vtkMultiBlockDataSet> mainDomainBlock;
1185 auto domains = m_f->m_graph->GetDomain();
1186 std::vector<vtkNew<vtkMultiBlockDataSet>> domainMultiBlocks(domains.size());
1187 for (int i = 0; i < domains.size(); ++i)
1188 {
1189 auto dom = domains[i];
1190
1191 // Loop over composites and see if in domain
1192 for (auto &comp : composites)
1193 {
1194 int compId = comp.first;
1195 if (dom.find(compId) != dom.end())
1196 {
1197 unsigned int nBlock = domainMultiBlocks[i]->GetNumberOfBlocks();
1198 domainMultiBlocks[i]->SetBlock(nBlock,
1199 vtkMesh[compId].GetPointer());
1200 domainMultiBlocks[i]->GetMetaData(nBlock)->Set(
1201 vtkCompositeDataSet::NAME(),
1202 compositeNames[compId].c_str());
1203 compSet.insert(compId);
1204 }
1205 }
1206
1207 unsigned int nBlock = mainDomainBlock->GetNumberOfBlocks();
1208 mainDomainBlock->SetBlock(nBlock, domainMultiBlocks[i].GetPointer());
1209 mainDomainBlock->GetMetaData(nBlock)->Set(
1210 vtkCompositeDataSet::NAME(),
1211 ("Domain ID " + std::to_string(i)).c_str());
1212 }
1213
1214 if (mainDomainBlock->GetNumberOfBlocks() != 0)
1215 {
1216 auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1217 mainBlock->SetBlock(nBlock, mainDomainBlock.GetPointer());
1218 mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1219 "Domains");
1220 }
1221
1222 // Fill boundary multi blocks from composites
1224 m_f->m_exp[0]->GetGraph());
1226 bcs.GetBoundaryRegions();
1227
1228 vtkNew<vtkMultiBlockDataSet> mainBoundaryBlock;
1229 std::vector<vtkNew<vtkMultiBlockDataSet>> boundaryMultiBlocks(
1230 bregions.size());
1231 int cnt = 0;
1232 for (auto &boundary : bregions)
1233 {
1234 // Loop over composites and see if in boundary
1235 for (auto &comp : composites)
1236 {
1237 int compId = comp.first;
1238 if (boundary.second->find(compId) != boundary.second->end())
1239 {
1240 unsigned int nBlock =
1241 boundaryMultiBlocks[cnt]->GetNumberOfBlocks();
1242 boundaryMultiBlocks[cnt]->SetBlock(
1243 nBlock, vtkMesh[compId].GetPointer());
1244 boundaryMultiBlocks[cnt]->GetMetaData(nBlock)->Set(
1245 vtkCompositeDataSet::NAME(),
1246 compositeNames[compId].c_str());
1247 compSet.insert(compId);
1248 }
1249 }
1250
1251 unsigned int nBlock = mainBoundaryBlock->GetNumberOfBlocks();
1252 mainBoundaryBlock->SetBlock(nBlock,
1253 boundaryMultiBlocks[cnt++].GetPointer());
1254
1255 // Set name as boundary label if it exists otherwise index ID
1256 std::string name = "Boundary ID " + std::to_string(boundary.first);
1257 auto blabels = bcs.GetBoundaryLabels();
1258 auto oIt = blabels.find(boundary.first);
1259 if (oIt != blabels.end())
1260 {
1261 name = oIt->second;
1262 }
1263
1264 mainBoundaryBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1265 name.c_str());
1266 }
1267
1268 if (mainBoundaryBlock->GetNumberOfBlocks() != 0)
1269 {
1270 auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1271 mainBlock->SetBlock(nBlock, mainBoundaryBlock.GetPointer());
1272 mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1273 "Boundaries");
1274 }
1275
1276 // Include all other composites (not domains or boundaries)
1277 vtkNew<vtkMultiBlockDataSet> compsMultiBlocks;
1278 cnt = 0;
1279 for (auto &comp : composites)
1280 {
1281 int compId = comp.first;
1282 if (compSet.find(compId) == compSet.end())
1283 {
1284 unsigned int nBlock = compsMultiBlocks->GetNumberOfBlocks();
1285 compsMultiBlocks->SetBlock(nBlock, vtkMesh[compId].GetPointer());
1286 compsMultiBlocks->GetMetaData(nBlock)->Set(
1287 vtkCompositeDataSet::NAME(), compositeNames[compId].c_str());
1288 }
1289 }
1290
1291 if (compsMultiBlocks->GetNumberOfBlocks() != 0)
1292 {
1293 auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1294 mainBlock->SetBlock(nBlock, compsMultiBlocks.GetPointer());
1295 mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1296 "Other composites");
1297 }
1298
1299 WriteVTK(mainBlock.GetPointer(), filename, vm);
1300}
1301
1302vtkSmartPointer<vtkUnstructuredGrid> OutputVtk::OutputFromExpHighOrder(
1303 po::variables_map &vm)
1304{
1305 ASSERTL0(
1306 m_f->m_numHomogeneousDir == 0,
1307 "High order VTK is not implemented for homogeneous expansion types.")
1308
1309 // Save shapetypes before convert to equispaced because that nukes the
1310 // explist
1311 std::vector<LibUtilities::ShapeType> sType;
1312 for (auto &i : *m_f->m_exp[0]->GetExp())
1313 {
1314 sType.emplace_back(i->GetGeom()->GetShapeType());
1315 }
1316
1317 // Convert expansion to an equispaced points field
1318 auto equispaced =
1320 equispaced->Process(vm);
1321
1322 // Insert points
1323 vtkNew<vtkPoints> vtkPoints;
1324 LibUtilities::PtsFieldSharedPtr fPts = m_f->m_fieldPts;
1325 vtkPoints->SetDataType(VTK_DOUBLE);
1326
1328 fPts->GetPts(pts);
1329
1330 int dim = static_cast<int>(fPts->GetDim());
1331 int nPts = static_cast<int>(fPts->GetNpoints());
1332 switch (dim)
1333 {
1334 case 3:
1335 for (int i = 0; i < nPts; ++i)
1336 {
1337 vtkPoints->InsertNextPoint(pts[0][i], pts[1][i], pts[2][i]);
1338 }
1339 break;
1340 case 2:
1341 for (int i = 0; i < nPts; ++i)
1342 {
1343 vtkPoints->InsertNextPoint(pts[0][i], pts[1][i], 0.0);
1344 }
1345 break;
1346 case 1:
1347 for (int i = 0; i < nPts; ++i)
1348 {
1349 vtkPoints->InsertNextPoint(pts[0][i], 0.0, 0.0);
1350 }
1351 break;
1352 default:
1353 break;
1354 }
1355
1356 // Mesh file to output
1357 vtkSmartPointer<vtkUnstructuredGrid> vtkMesh =
1358 vtkSmartPointer<vtkUnstructuredGrid>::New();
1359 vtkMesh->SetPoints(vtkPoints.GetPointer());
1360
1361 // Cache ordering for shape type & npts so we aren't recreating mappings
1362 std::unordered_map<size_t, std::vector<long long>> mappingCache;
1363
1364 // Get offset per element in a vector from ppe
1365 std::vector<int> ppe = m_f->m_fieldPts->GetPointsPerElement();
1366 Array<OneD, int> ppeOffset(ppe.size() + 1, 0.0);
1367 std::partial_sum(ppe.begin(), ppe.end(), &ppeOffset[1]);
1368
1369 // Insert elements
1370 for (int i = 0; i < ppe.size(); ++i)
1371 {
1372 // Construct inverse of input reordering.
1373 // First try to find it in our mapping cache.
1374 auto oIt = mappingCache.find(key2(sType[i], ppe[i]));
1375 if (oIt == mappingCache.end())
1376 {
1377 std::vector<long long> p(ppe[i]);
1378 std::iota(p.begin(), p.end(), 0);
1379 switch (sType[i])
1380 {
1382 p = quadTensorNodeOrdering(p);
1383 break;
1385 p = triTensorNodeOrdering(p);
1386 break;
1388 p = tetTensorNodeOrdering(p);
1389 break;
1391 p = hexTensorNodeOrdering(p);
1392 break;
1394 p = prismTensorNodeOrdering(p);
1395 break;
1396 default:
1398 "High-order VTU output not set up for the " +
1399 static_cast<std::string>(
1400 LibUtilities::ShapeTypeMap[sType[i]]) +
1401 " shape type.");
1402 break;
1403 }
1404
1405 // Invert the ordering as this is for spectral -> recursive (VTU)
1406 std::vector<long long> inv(ppe[i]);
1407 for (int j = 0; j < ppe[i]; ++j)
1408 {
1409 inv[p[j]] = j;
1410 }
1411
1412 oIt =
1413 mappingCache.insert(std::make_pair(key2(sType[i], ppe[i]), inv))
1414 .first;
1415 }
1416
1417 // Add offset to reordering
1418 std::vector<long long> p = oIt->second;
1419 for (long long &j : p)
1420 {
1421 j += ppeOffset[i];
1422 }
1423
1424 vtkMesh->InsertNextCell(GetHighOrderVtkCellType(sType[i]), ppe[i],
1425 &p[0]);
1426 }
1427
1428 return vtkMesh;
1429}
1430
1432 po::variables_map &vm, std::string &filename,
1433 vtkSmartPointer<vtkUnstructuredGrid> &vtkMesh)
1434{
1435 // Convert expansion to an equispaced points field
1436 if (m_cachedMesh)
1437 {
1438 auto equispaced =
1440 equispaced->Process(vm);
1441 }
1442 LibUtilities::PtsFieldSharedPtr fPts = m_f->m_fieldPts;
1443
1444 int nPts = static_cast<int>(fPts->GetNpoints());
1445 int dim = static_cast<int>(fPts->GetDim());
1446
1448 fPts->GetPts(pts);
1449
1450 // Insert field information
1451 for (int i = 0; i < fPts->GetNFields(); ++i)
1452 {
1453 vtkNew<vtkDoubleArray> fieldData;
1454 fieldData->SetArray(&pts[dim + i][0], nPts, 1);
1455 fieldData->SetName(&fPts->GetFieldName(i)[0]);
1456 vtkMesh->GetPointData()->AddArray(fieldData.GetPointer());
1457 }
1458
1459 WriteVTK(vtkMesh, filename, vm);
1460}
1461
1462void OutputVtk::WriteVTK(vtkDataObject *vtkMesh, std::string &filename,
1463 po::variables_map &vm)
1464{
1465 // Initialise base writer class as default structured grid writer
1466 vtkSmartPointer<vtkXMLWriter> vtkMeshWriter =
1467 vtkNew<vtkXMLUnstructuredGridWriter>().GetPointer();
1468
1469 // If multi block we need to switch to the MultiBlock writer
1470 if (m_config["multiblock"].m_beenSet)
1471 {
1472 vtkMeshWriter = vtkNew<vtkXMLMultiBlockDataWriter>().GetPointer();
1473 }
1474
1475 // Choose the correct extension based on which mesh writer is used
1476 int dot = static_cast<int>(filename.find_last_of('.'));
1477 std::string body = filename.substr(0, dot + 1);
1478 filename = body + vtkMeshWriter->GetDefaultFileExtension();
1479
1480 // Set time information if exists
1481 if (!m_f->m_fieldMetaDataMap["Time"].empty())
1482 {
1483 vtkMesh->GetInformation()->Set(
1484 vtkDataObject::DATA_TIME_STEP(),
1485 std::stod(m_f->m_fieldMetaDataMap["Time"]));
1486 }
1487 else if (!m_f->m_fieldMetaDataMap["FinalTime"].empty())
1488 {
1489 vtkMesh->GetInformation()->Set(
1490 vtkDataObject::DATA_TIME_STEP(),
1491 std::stod(m_f->m_fieldMetaDataMap["FinalTime"]));
1492 }
1493
1494 // Write out the new mesh in XML format (don't support legacy
1495 // format here as we still have standard OutputVtk.cpp)
1496 vtkMeshWriter->SetFileName(filename.c_str());
1497
1498#if VTK_MAJOR_VERSION <= 5
1499 vtkMeshWriter->SetInput(vtkMesh);
1500#else
1501 vtkMeshWriter->SetInputData(vtkMesh);
1502#endif
1503
1504 if (m_config["uncompress"].m_beenSet)
1505 {
1506 vtkMeshWriter->SetDataModeToAscii();
1507 }
1508
1509 vtkMeshWriter->Update();
1510
1511 std::cout << "Written file: " << filename << std::endl;
1512
1513 // Output parallel file information if needed - using a manual write.
1514 // We could use the VTK lib to do this, but that requires VTK with MPI
1515 // enabled & messing about with the parallel controller & changing
1516 // our file naming scheme as VTK forces _${proc-number} as a suffix...
1517 if (m_f->m_comm->GetSpaceComm()->TreatAsRankZero() &&
1518 !m_f->m_comm->GetSpaceComm()->IsSerial())
1519 {
1520 WritePVtu(vm);
1521 }
1522}
1523
1524void OutputVtk::WritePVtu(po::variables_map &vm)
1525{
1526 std::string filename = m_config["outfile"].as<std::string>();
1527 int dot = static_cast<int>(filename.find_last_of('.'));
1528 std::string body = filename.substr(0, dot);
1529 filename = body + ".pvtu";
1530
1531 std::ofstream outfile(filename.c_str());
1532
1533 int nprocs = m_f->m_comm->GetSpaceComm()->GetSize();
1534 std::string path =
1536
1537 outfile << "<?xml version=\"1.0\"?>" << endl;
1538 outfile << "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\" "
1539 << "byte_order=\"LittleEndian\">" << endl;
1540 outfile << " <PUnstructuredGrid GhostLevel=\"0\">" << endl;
1541
1542 // Add time if exists
1543 if (!m_f->m_fieldMetaDataMap["Time"].empty())
1544 {
1545 outfile << " <FieldData> " << endl;
1546 outfile << " <DataArray type=\"Float64\" Name=\"TimeValue\" "
1547 "NumberOfTuples=\"1\" format=\"ascii\">"
1548 << endl;
1549 outfile << m_f->m_fieldMetaDataMap["Time"] << std::endl;
1550 outfile << " </DataArray>" << std::endl;
1551 outfile << " </FieldData> " << endl;
1552 }
1553
1554 // Add point coordinates
1555 outfile << " <PPoints> " << endl;
1556 outfile << " <PDataArray type=\"Float64\" NumberOfComponents=\"" << 3
1557 << "\"/> " << endl;
1558 outfile << " </PPoints>" << endl;
1559
1560 // Add cell information
1561 outfile << " <PCells>" << endl;
1562 outfile << " <PDataArray type=\"Int64\" Name=\"connectivity\" "
1563 "NumberOfComponents=\"1\"/>"
1564 << endl;
1565 outfile << " <PDataArray type=\"Int64\" Name=\"offsets\" "
1566 "NumberOfComponents=\"1\"/>"
1567 << endl;
1568 outfile << " <PDataArray type=\"UInt8\" Name=\"types\" "
1569 "NumberOfComponents=\"1\"/>"
1570 << endl;
1571 outfile << " </PCells>" << endl;
1572
1573 // Add fields (point data)
1574 outfile << " <PPointData>" << endl;
1575 for (auto &var : m_f->m_variables)
1576 {
1577 outfile << " <PDataArray type=\"Float64\" Name=\"" << var << "\"/>"
1578 << endl;
1579 }
1580 outfile << " </PPointData>" << endl;
1581
1582 // Add parallel files
1583 for (int i = 0; i < nprocs; ++i)
1584 {
1585 boost::format pad("P%1$07d.vtu");
1586 pad % i;
1587 outfile << " <Piece Source=\"" << path << "/" << pad.str() << "\"/>"
1588 << endl;
1589 }
1590 outfile << " </PUnstructuredGrid>" << endl;
1591 outfile << "</VTKFile>" << endl;
1592
1593 cout << "Written file: " << filename << endl;
1594}
1595
1596void OutputVtk::v_OutputFromData(po::variables_map &vm)
1597{
1598 boost::ignore_unused(vm);
1600 "OutputVtk can't write using only FieldData. You may need "
1601 "to add a mesh XML file to your input files.");
1602}
1603
1604void OutputVtk::v_OutputFromPts(po::variables_map &vm)
1605{
1607}
1608
1609void OutputVtk::v_OutputFromExp(po::variables_map &vm)
1610{
1611 if (m_config["legacy"].m_beenSet)
1612 {
1613 ASSERTL0(!m_config["multiblock"].m_beenSet,
1614 "Multi block VTK is not implemented for legacy output.")
1615
1616 ASSERTL0(!m_config["highorder"].m_beenSet,
1617 "High order VTK is not implemented for legacy output.")
1618
1619 // No caching of mesh data in legacy output
1621 return;
1622 }
1623
1624 // Extract the output filename and extension
1625 std::string filename = OutputVtkBase::PrepareOutput(vm);
1626
1627 // Save mesh state (if using filter this allows us to only ProcessEquispaced
1628 // if needed)
1630
1631 if (m_config["highorder"].m_beenSet)
1632 {
1633 ASSERTL0(!m_config["multiblock"].m_beenSet,
1634 "Multi block VTK is not implemented for high-order output.")
1635
1636 if (!m_cachedMesh)
1637 {
1639 }
1640
1642 }
1643 else if (m_config["multiblock"].m_beenSet)
1644 {
1645 // No mesh caching for multiblock due to complexity of field data
1646 OutputFromExpLowOrderMultiBlock(vm, filename);
1647 }
1648 else
1649 {
1650 if (!m_cachedMesh)
1651 {
1653 }
1654
1655 AddFieldDataToVTKLowOrder(vm, filename, m_vtkMesh);
1656 }
1657}
1658
1659} // namespace FieldUtils
1660} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
FieldSharedPtr m_f
Field object.
Definition: Module.h:234
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:263
Converter from fld to vtk.
Definition: OutputVtkBase.h:48
std::string PrepareOutput(po::variables_map &vm)
virtual fs::path v_GetPath(std::string &filename, po::variables_map &vm) override
virtual void v_OutputFromExp(po::variables_map &vm) override
Write from m_exp to output file.
virtual void v_OutputFromPts(po::variables_map &vm) override
Write from pts to output file.
vtkSmartPointer< vtkUnstructuredGrid > OutputFromExpLowOrder()
Prepare low order VTK output.
Definition: OutputVtk.cpp:856
void AddFieldDataToVTKHighOrder(po::variables_map &vm, std::string &filename, vtkSmartPointer< vtkUnstructuredGrid > &vtkMesh)
Add field data to high order Lagrange VTK output.
Definition: OutputVtk.cpp:1431
void OutputFromExpLowOrderMultiBlock(po::variables_map &vm, std::string &filename)
Prepare low order multi-block VTK output & add field data.
Definition: OutputVtk.cpp:1023
OutputVtk(FieldSharedPtr f)
Definition: OutputVtk.cpp:59
vtkSmartPointer< vtkUnstructuredGrid > m_vtkMesh
Cache file for unstructured grid VTK mesh data.
Definition: OutputVtk.h:82
bool m_extraPlane
Flag if extra plane in case of fourier expansion in homogeneous dir.
Definition: OutputVtk.h:88
int m_numPlanes
Number of planes if homogeneous.
Definition: OutputVtk.h:85
virtual void v_OutputFromPts(po::variables_map &vm) override final
Write from pts to output file.
Definition: OutputVtk.cpp:1604
void AddFieldDataToVTKLowOrder(po::variables_map &vm, std::string &filename, vtkSmartPointer< vtkUnstructuredGrid > &vtkMesh)
Add field data to low order VTK output.
Definition: OutputVtk.cpp:979
void WriteVTK(vtkDataObject *vtkMesh, std::string &filename, po::variables_map &vm)
Write VTK file using.
Definition: OutputVtk.cpp:1462
vtkSmartPointer< vtkUnstructuredGrid > OutputFromExpHighOrder(po::variables_map &vm)
Prepare high order Lagrange VTK output.
Definition: OutputVtk.cpp:1302
static ModuleKey m_className
Definition: OutputVtk.h:60
virtual void v_OutputFromData(po::variables_map &vm) override final
Write from data to output file.
Definition: OutputVtk.cpp:1596
virtual void v_OutputFromExp(po::variables_map &vm) override final
Write from m_exp to output file.
Definition: OutputVtk.cpp:1609
bool m_cachedMesh
Flag if mesh has been cached.
Definition: OutputVtk.h:91
static std::shared_ptr< Module > create(const FieldSharedPtr &f)
Creates an instance of this class.
Definition: OutputVtk.h:55
void WritePVtu(po::variables_map &vm)
Write the parallel .pvtu file.
Definition: OutputVtk.cpp:1524
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:198
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
const std::map< int, std::string > & GetBoundaryLabels(void) const
Definition: Conditions.h:265
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:234
def copy(self)
Definition: pycml.py:2663
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:991
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:317
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:49
const char *const ShapeTypeMap[SIZE_ShapeType]
Definition: ShapeType.hpp:79
std::string PortablePath(const boost::filesystem::path &path)
create portable path on different platforms for boost::filesystem path
Definition: FileSystem.cpp:45
std::shared_ptr< PtsField > PtsFieldSharedPtr
Definition: PtsField.h:190
@ eFourierEvenlySpaced
1D Evenly-spaced points using Fourier Fit
Definition: PointsType.h:76
@ eFourier
Fourier Expansion .
Definition: BasisType.h:57
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:210
std::tuple< unsigned int, unsigned int, unsigned int, unsigned int > Mode
Definition: StdPyrExp.h:47
std::vector< double > z(NPUPPER)
std::vector< double > d(NPUPPER *NPUPPER)
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble
void Fill(int n, const T alpha, T *x, const int incx)
Fill a vector with a constant value.
Definition: Vmath.cpp:43
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1191
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:131